RedisShake 介绍

阿里开源的 Redis 迁移工具,开源协议 MIT。也可用作备份恢复等场景。可用于跨版本迁移数据,跨拓扑迁移数据,例如从单机 Redis 数据迁移到 Redis Cluster 集群中。但是鉴于 Redis 一般做缓存使用,实际应用数据迁移的场景并不十分多。

GitHub 地址:https://github.com/tair-opensource/RedisShake.git

image

使用示例

使用方法非常简单,配置 shake.toml 文件,运行 redis-shake shake.toml 即可。

postgres@slpc:~/redisshake$ ./redis-shake shake.toml
2026-03-05 11:02:50 INF load config from file: shake.toml
2026-03-05 11:02:50 INF log_level: [info], log_file: [/home/postgres/redisshake/data/shake.log]
2026-03-05 11:02:50 INF changed work dir. dir=[/home/postgres/redisshake/data]
2026-03-05 11:02:50 INF GOMAXPROCS defaults to the value of runtime.NumCPU [8]
2026-03-05 11:02:50 INF not set pprof port
2026-03-05 11:02:50 INF create SyncClusterReader
2026-03-05 11:02:50 INF * address (should be the address of one node in the Redis cluster): 192.168.232.128:6379
2026-03-05 11:02:50 INF * username: admin
2026-03-05 11:02:50 INF * password: **************
2026-03-05 11:02:50 INF * tls: false
2026-03-05 11:02:50 INF address=192.168.232.128:6379, reply=ca62daac7e5028e18f65def8a3ffdfb519c7be63 192.168.232.138:6379@16379 master - 0 1772679768763 3 connected 10923-16383
831e4c891d2025233320461e4b6aa3ea3e9c3c97 192.168.232.128:6379@16379 myself,master - 0 1772679768000 1 connected 0-5460
e0856d63d6a1170c0755f74782ba828be23e8fc2 192.168.232.137:6379@16379 master - 0 1772679769768 2 connected 5461-10922
2026-03-05 11:02:50 INF NewFileWriter absolute path=[/home/postgres/redisshake/cmd.dump],type=[cmd]
2026-03-05 11:02:50 INF start syncing...
2026-03-05 11:02:50 INF [reader_192.168.232.138_6379] source db is not doing bgsave! continue.
2026-03-05 11:02:50 INF [reader_192.168.232.128_6379] source db is not doing bgsave! continue.
2026-03-05 11:02:50 INF [reader_192.168.232.137_6379] source db is not doing bgsave! continue.
2026-03-05 11:03:00 INF read_count=[0], read_ops=[0.00], write_count=[0], write_ops=[0.00], src-2, waiting bgsave
2026-03-05 11:03:05 INF read_count=[0], read_ops=[0.00], write_count=[0], write_ops=[0.00], src-0, waiting bgsave
2026-03-05 11:03:10 INF read_count=[0], read_ops=[0.00], write_count=[0], write_ops=[0.00], src-1, waiting bgsave
2026-03-05 11:03:15 INF read_count=[0], read_ops=[0.00], write_count=[0], write_ops=[0.00], src-2, receiving rdb, size=[654 MiB/2.0 GiB]
2026-03-05 11:03:20 INF read_count=[0], read_ops=[0.00], write_count=[0], write_ops=[0.00], src-0, receiving rdb, size=[1000 MiB/2.0 GiB]
2026-03-05 11:03:25 INF read_count=[0], read_ops=[0.00], write_count=[0], write_ops=[0.00], src-1, receiving rdb, size=[1.6 GiB/2.0 GiB]
2026-03-05 11:03:30 INF read_count=[206046], read_ops=[41209.00], write_count=[206046], write_ops=[41209.00], src-2, receiving rdb, size=[1.8 GiB/2.0 GiB]
2026-03-05 11:03:35 INF read_count=[831945], read_ops=[125182.55], write_count=[831942], write_ops=[125182.15], src-0, syncing rdb, size=[158 MiB/2.0 GiB]
2026-03-05 11:03:40 INF read_count=[1664476], read_ops=[166505.37], write_count=[1664473], write_ops=[166505.37], src-1, syncing rdb, size=[759 MiB/2.0 GiB]
2026-03-05 11:03:45 INF read_count=[2577500], read_ops=[182606.22], write_count=[2577497], write_ops=[182606.22], src-2, syncing rdb, size=[662 MiB/2.0 GiB]
2026-03-05 11:03:50 INF read_count=[3303362], read_ops=[145171.40], write_count=[3303359], write_ops=[145171.40], src-0, syncing rdb, size=[974 MiB/2.0 GiB]
2026-03-05 11:03:55 INF read_count=[3955934], read_ops=[130507.06], write_count=[3955931], write_ops=[130507.06], src-1, syncing rdb, size=[1.5 GiB/2.0 GiB]
2026-03-05 11:04:00 INF read_count=[4609175], read_ops=[130650.84], write_count=[4609172], write_ops=[130650.84], src-2, syncing rdb, size=[1.3 GiB/2.0 GiB]
2026-03-05 11:04:05 INF read_count=[5269922], read_ops=[132155.14], write_count=[5269919], write_ops=[132155.14], src-0, syncing rdb, size=[1.6 GiB/2.0 GiB]
2026-03-05 11:04:10 INF read_count=[5946592], read_ops=[135333.63], write_count=[5946590], write_ops=[135333.83], src-1, syncing rdb, size=[2.0 GiB/2.0 GiB]
2026-03-05 11:04:11 INF all done

image

源端读

通过 sync 的方式读取源端数据,即通过 PSYNC 协议,类似以从节点的方式获取源节点的数据(rdb+aof)。

[sync_reader]
cluster = true            # 当源端为 Redis 集群时设置为 true
address = "192.168.232.128:6379" # 集群场景填写任意一个集群节点地址;主从场景填写主节点或从节点地址
username = "admin"              # 未启用 ACL 时留空
password = "admin_password"     # 无需鉴权时留空
tls = false                     # 需要 TLS 时设置为 true
sync_rdb = true                 # 不需要同步 RDB 时设置为 false
sync_aof = false                # 不需要同步 AOF 时设置为 false
prefer_replica = false          # 设置为 true 可从副本节点同步
try_diskless = false            # 源端配置了 repl-diskless-sync=yes 时设置为 true 可进行无盘同步

image

通过 SCAN 方式读取源端数据,通过 SCAN 命令,不断获取 key,相当于扫描源节点所有的 key。

[scan_reader]
cluster = false            # 源端为 redis 集群时设置为 true
address = "127.0.0.1:6379" # 集群场景设置为其中一个集群节点地址
username = ""              # 未使用 ACL 时留空
password = ""              # 无需鉴权时留空
tls = false
dbs = []                   # 设置需要扫描的 db,例如 [1,5,7],不设置则扫描所有
scan = true                # 不需要扫描 key 时设置为 false
ksn = false                # 设置为 true 可启用 Redis keyspace notifications (KSN) 订阅
count = 1                  # 每次迭代扫描的 key 数量
## RedisShake 使用 TYPE 命令获取每个 key 的类型。
## 如果类型在此跳过列表中,则跳过该 key。
skip_unknown_type = []  # 例如 ["imset", "mbloom", "string", "hash", "list", "set", "zset"]

image

通过 rdb 的方式读取数据快照获取数据。

[rdb_reader]
filepath = "/tmp/dump.rdb"

通过 aof 的方式读取源端数据。

[aof_reader]
aoffilepath="/tmp/appendonly.aof.manifest" # 或者单 aof 文件 "/tmp/appendonly.aof"
aoftimestamp="0"

image

目标端写

源端采集的数据写到哪里?redis-shake 支持写到 Redis 或者文件中。其写入文件或者 Redis 中,并不是采用 RDB 的方式,而是类似于重放 Redis 命令的方式实现。

Redis Writer 介绍

redis_writer 用于将数据写入 Redis-like 数据库。数据迁移时是写入目标数据库中,在恢复数据时是将数据恢复到 Redis 数据库中。

配置
[redis_writer]
cluster = false
address = "127.0.0.1:6379" # 集群场景,address 为任意一个集群节点地址
username = ""              # 未使用 ACL 时留空
password = ""              # 无需鉴权时留空
tls = false
  • cluster:是否为集群。
  • address:连接地址。当目的端为集群时,address 填写集群中的任意一个节点即可。
  • 鉴权:
    • 当使用 ACL 账号体系时,配置 usernamepassword
    • 当使用传统账号体系时,仅配置 password
    • 当无鉴权时,不配置 usernamepassword
  • tls:是否开启 TLS/SSL,不需要配置证书,因为 RedisShake 没有校验服务器证书。

注意事项:

  1. 当目的端为集群时,应保证源端发过来的命令满足 Key 的哈希值属于同一个 slot
  2. 应尽量保证目的端版本大于等于源端版本,否则可能会出现不支持的命令。如确实需要降低版本,可以设置 target_redis_proto_max_bulk_len 为 0,来避免使用 restore 命令恢复数据。

file_writer 介绍

可以使用 file_writer 写文件,可写的格式有 CMD/JSON/AOF,常用于通过文件介质抽取/迁移/订正数据。

其中 cmd 格式为命令格式,会将源端数据转换成命令写入文件中。不管中间执行了什么命令,其实到最后进行恢复的时候,都是可以用 set 等命令插入 key 的形式进行恢复。

配置
[file_writer]
filepath = "/tmp/cmd.txt"
type = "cmd" # cmd,aof,json (默认 cmd)
  • 绝对路径 filepath 是必填的。
应用场景
  • 两系统共享数据:一个系统把文件写到 disk/s3/oss,另一系统从中读取。
  • 跨系统局部迁移带指定前缀的数据:从 A 系统迁出带前缀 “XXX:” 的数据,B 系统通过命令导入这些数据 redis-cli --pipe XXX.aof
  • 通过命令文件订正数据:从一个系统中导出数据成 cmd 格式,订正后再导入命令 redis-cli < cmd.txt
  • 通过 json 格式做数据分析:导出成 json 文件,导入到 mongodb/bi 做分析。
示例输出
cmd_writer 输出:
SELECT 0
set key1 1
set key2 2
set key3 3
sadd key4 1 2 3 4
lpush key5 1 2 3 4 5
zadd key6 1 2 3 4 5 6
json_writer 输出:
{"DbId":0,"Argv":["SELECT","0"],"CmdName":"SELECT","Group":"CONNECTION","Keys":null,"KeyIndexes":null,"Slots":[],"SerializedSize":23}
{"DbId":0,"Argv":["set","key1","1"],"CmdName":"SET","Group":"STRING","Keys":["key1"],"KeyIndexes":[2],"Slots":[9189],"SerializedSize":30}
{"DbId":0,"Argv":["set","key2","2"],"CmdName":"SET","Group":"STRING","Keys":["key2"],"KeyIndexes":[2],"Slots":[4998],"SerializedSize":30}
{"DbId":0,"Argv":["set","key3","3"],"CmdName":"SET","Group":"STRING","Keys":["key3"],"KeyIndexes":[2],"Slots":[935],"SerializedSize":30}
{"DbId":0,"Argv":["sadd","key4","1","2","3","4"],"CmdName":"SADD","Group":"SET","Keys":["key4"],"KeyIndexes":[2],"Slots":[13120],"SerializedSize":52}
{"DbId":0,"Argv":["lpush","key5","1","2","3","4","5"],"CmdName":"LPUSH","Group":"LIST","Keys":["key5"],"KeyIndexes":[2],"Slots":[9057],"SerializedSize":60}
{"DbId":0,"Argv":["zadd","key6","1","2","3","4","5","6"],"CmdName":"ZADD","Group":"SORTED_SET","Keys":["key6"],"KeyIndexes":[2],"Slots":[4866],"SerializedSize":66}
aof_writer 输出:
*2
$6
SELECT
$1
0
*3
$3
set
$4
key1
$1
1