Redis 官方工具(redis-dump、RDB 備份、主從複寫)通常針對整個實例。當只需遷移特定 DB(如 DB 7)且線上服務不能停機時,自訂 PHP 腳本逐鍵遷移最靈活。
<?php
/**
* Redis 指定 DB 遷移工具
* 用法: php migrate.php source_dsn target_dsn [db_number]
* 例: php migrate.php 10.0.2.52:6379/7 127.0.0.1:6379/7
*/
declare(strict_types=1);
$fromDsn = $argv[1] ?? '10.0.2.52:6379/7';
$toDsn = $argv[2] ?? '127.0.0.1:6379/7';
$from = connectRedis($fromDsn);
$to = connectRedis($toDsn);
$keys = $from->keys('*');
$total = count($keys);
$migrated = 0;
$skipped = 0;
$errors = 0;
echo "開始遷移:$total 把鍵從 $fromDsn → $toDsn\n";
foreach ($keys as $key) {
$migrated++;
if ($migrated % 100 === 1) {
echo "進度: $migrated/$total\n";
}
$type = $from->type($key);
try {
switch ($type) {
case Redis::REDIS_STRING:
$val = $from->get($key);
$to->set($key, $val);
break;
case Redis::REDIS_LIST:
$list = $from->lRange($key, 0, -1);
if ($list) {
$to->del($key); // 確保目標為空
$to->rPush($key, ...$list); // PHP 5.6+ 解包陣列
}
break;
case Redis::REDIS_HASH:
$hash = $from->hGetAll($key);
if ($hash) {
$to->hMSet($key, $hash);
}
break;
case Redis::REDIS_SET:
$members = $from->sMembers($key);
if ($members) {
$to->sAdd($key, ...$members);
}
break;
case Redis::REDIS_ZSET:
$zset = $from->zRange($key, 0, -1, true); // withscores
if ($zset) {
foreach ($zset as $member => $score) {
$to->zAdd($key, $score, $member);
}
}
break;
default:
$skipped++;
echo "跳過不支援型別 [$type]: $key\n";
}
} catch (Throwable $e) {
$errors++;
echo "遷移失敗 [$key]: " . $e->getMessage() . "\n";
}
}
echo "\n遷移完成:成功 $migrated,跳過 $skipped,錯誤 $errors\n";
function connectRedis(string $dsn): Redis {
$redis = new Redis();
// 解析 host:port/db
if (!preg_match('/^([^:]+):(\d+)\/(\d+)$/', $dsn, $m)) {
throw new InvalidArgumentException("DSN 格式錯誤,應為 host:port/db");
}
[$_, $host, $port, $db] = $m;
$redis->connect($host, (int)$port);
$redis->select((int)$db);
return $redis;
}
| 資料型別 | 讀取指令 | 寫入指令 | 注意事項 |
|---|---|---|---|
| String | GET | SET | 大值建議用 GETRANGE 分塊讀寫 |
| List | LRANGE 0 -1 | RPUSH (批次) | 保持順序,del 先清空目標 |
| Hash | HGETALL | HMSET | 單指令寫入所有欄位 |
| Set | SMEMBERS | SADD (批次) | 無序去重 |
| ZSet | ZRANGE 0 -1 WITHSCORES | ZADD (逐個) | 需保留分數,批次 ZADD 需 Redis 6.2+ |
KEYS *,改用 SCAN 迭代避免阻塞。
$it = null;
while ($keys = $from->scan($it, '*', 1000)) {
foreach ($keys as $key) { /* 遷移邏輯 */ }
}
$pipe = $to->multi(Redis::PIPELINE);
foreach ($list as $v) $pipe->rPush($key, $v);
$pipe->exec();
TTL $key,遷移後 EXPIRE $key $ttl。TYPE、STRLEN、LLEN、HLEN、SCARD、ZCARD。若源或目標為 Redis Cluster,需使用 phpredis 5.3+ 的 RedisCluster 類別,並注意 KEYS/SCAN 需在每個分片執行,或用 CLUSTER KEYSLOT 計算槽位。
自訂 PHP 遷移腳本提供最大彈性:可過濾鍵前綴、轉換資料結構、保留 TTL、斷點續傳。搭配 SCAN + PIPELINE 可平穩遷移千萬級鍵值,是 Redis 運維工具箱必備技能。