从 Redis 迁移
使用 RedisShake 将 Redis 或 Redis Cluster 迁移至 Lavik 0.2.0:复制初始数据、同步增量写入、验证追平及副本,最后切换应用。
使用 RedisShake 迁移
使用 RedisShake v4.6.2 的 sync_reader 与 redis_writer。工具读取 Redis 的初始 RDB 快照,将其恢复到 Lavik,然后持续转发源复制流。导入期间应用可继续写入 Redis;最终验证和切换需要预留暂停写入的窗口。本页同时涵盖 Redis 与 Redis Cluster 源。
本流程面向全新的 Meta 托管 Lavik Single 或 Cluster 部署。导入前创建目标及其副本;RedisShake 向当前 Group Owner 写入,再由 Lavik 复制这些写入。通过 REPLICAOF 导入非 Meta 节点属于另一种部署路径。
| 源 → 目标 | Reader `cluster` | Writer `cluster` | 数据库范围 |
|---|---|---|---|
| Redis → Lavik Single | false | false | DB0–15 |
| Redis → Lavik Cluster | false | true | DB0;多键操作必须同槽 |
| Redis Cluster → Lavik Single | true | false | DB0 |
| Redis Cluster → Lavik Cluster | true | true | DB0;多键操作必须同槽 |
这些开关描述两端的客户端协议,而非机器数量。Single 部署也可以带副本。源分片和目标 Group 无需一一对应。
1. 准备源与空目标
- 先使用真实工作负载的副本演练。本版本已验证的源为 Redis 7.2.14;其他版本需单独演练,尤其是更新的 RDB 编码、模块类型、Lua Functions 和 Streams。
- 盘点每个源主节点、逻辑 DB、数据类型、命令和 Function 库。Lavik Single 支持 DB0–15;Cluster 仅支持 DB0。选择 Cluster 目标前,先处理非零 DB 和跨槽应用操作,不要用过滤器静默丢弃数据库或命令。
- 使用下方部署指南创建隔离的空 Lavik 目标。要求每个 Group 可服务,全部预期副本均已同步且可读。所有目标节点使用 v0.2.0。切换前禁止应用读写目标。
- 迁移主机需要 Linux x86_64 或 ARM64、Bash、curl、tar、sha256sum、Python 3 和 redis-cli。为 RDB 快照及增量缓冲预留磁盘空间;演练时观察磁盘使用、源快照负载、复制积压与目标内存。
- 允许工具认证、读取复制流、发现源拓扑,并向目标写入所需命令。托管 Redis 服务可能限制 PSYNC;安排迁移前先确认可用性。整个迁移期间保持源拓扑稳定。
以下回环示例使用 127.0.0.1:6379 上的 Redis(Cluster 种子为 127.0.0.1:7000)及 127.0.0.1:6371 上的 Lavik,与二进制部署指南一致。请替换为迁移进程可访问的端点;Docker 中的 127.0.0.1 指当前容器,不是另一个容器或宿主机。
非回环流量应为所有源、目标连接配置对端认证的加密隧道,包括发现的 Cluster 节点。RedisShake v4.6.2 的 tls = true 不验证服务端证书。状态监听器绑定所有接口:应阻止外部访问 18080 端口,仅在本机查询。
2. 安装 RedisShake v4.6.2
在迁移主机的 Bash 中运行以下命令。它会选择 Linux 架构,用固定的 SHA-256 校验发布包,并将二进制安装到用户目录。保留此终端以继续使用 SHAKE_BIN。
# Run in Bash on a Linux migration host with curl, tar and sha256sum.
set -euo pipefail
case "$(uname -m)" in
x86_64)
arch=amd64
checksum=c255565a60c15f9d1b4d38ef46d30056161266ffa75e11065c7b1901e236bff7
;;
aarch64|arm64)
arch=arm64
checksum=5fc2b7a393f159f8981c1e8d712b63c0866628b99ad030ed3e46c9869c22ff04
;;
*) echo "Use a Linux x86_64 or ARM64 host" >&2; exit 1 ;;
esac
export SHAKE_BIN="$HOME/.local/share/redis-shake-4.6.2/redis-shake"
archive=$(mktemp)
curl --fail --location --retry 3 \
"https://github.com/tair-opensource/RedisShake/releases/download/v4.6.2/redis-shake-v4.6.2-linux-${arch}.tar.gz" \
--output "$archive"
printf '%s %s\n' "$checksum" "$archive" | sha256sum --check
mkdir -p "$(dirname "$SHAKE_BIN")"
tar -xzf "$archive" -C "$(dirname "$SHAKE_BIN")" ./redis-shake
rm "$archive"
test -x "$SHAKE_BIN"为本次迁移创建私有目录。如果选择其他目录或磁盘,请同时更新配置中的两个绝对路径。失败后不要复用旧缓冲目录。
# Use a new directory for EVERY attempt. Choose a disk with space for the spool.
set -euo pipefail
export MIGRATION_DIR=/var/tmp/lavik-import-01
umask 077
# Deliberately fail if this attempt directory already exists.
mkdir "$MIGRATION_DIR"
mkdir "$MIGRATION_DIR/spool"
touch "$MIGRATION_DIR/shake.toml"
chmod 600 "$MIGRATION_DIR/shake.toml"
# Paste ONE of the configurations below into this file and edit its endpoints.
${EDITOR:-vi} "$MIGRATION_DIR/shake.toml"3. 配置 Redis 源
将这份完整 TOML 粘贴到 shake.toml。它将 Redis 迁移至托管 Lavik Single。写入 Lavik Cluster 时,仅将 [redis_writer].cluster 改为 true。保留 sync_rdb 和 sync_aof,同时完成全量导入与增量追平。
[sync_reader]
address = "127.0.0.1:6379"
username = ""
password = "replace-source-password"
cluster = false
prefer_replica = false
tls = false
sync_rdb = true
sync_aof = true
[redis_writer]
address = "127.0.0.1:6371"
username = ""
password = "replace-target-password"
cluster = false
tls = false
off_reply = false
[advanced]
dir = "/var/tmp/lavik-import-01/spool"
log_file = "/var/tmp/lavik-import-01/shake.log"
status_port = 18080
log_interval = 1
rdb_restore_command_behavior = "panic"
target_redis_proto_max_bulk_len = 512000000将两端密码占位符替换为对应凭据。若 Redis 使用命名 ACL 用户,请设置源 username。Lavik 0.2.0 通过 requirepass 使用 default 用户;目标用户名留空或填写 default。仅在明确无需认证的本地演练中,将对应密码设为 ""。TOML 不会展开 shell 环境变量。配置文件权限应保持 0600,不要提交到 Git。
4. 配置 Redis Cluster 源
用这份完整配置替代上一份。工具通过一个种子发现源主节点,并向 Lavik Cluster 种子写入。若整合至托管 Lavik Single,仅将 [redis_writer].cluster 改为 false。不要拼接两份配置,也不要同时将两者运行在同一目标上。
[sync_reader]
# One source seed; every discovered primary must also be reachable.
address = "127.0.0.1:7000"
username = ""
password = "replace-source-password"
cluster = true
prefer_replica = false
tls = false
sync_rdb = true
sync_aof = true
[redis_writer]
# One target seed; every advertised Lavik endpoint must be reachable.
address = "127.0.0.1:6371"
username = ""
password = "replace-target-password"
cluster = true
tls = false
off_reply = false
[advanced]
dir = "/var/tmp/lavik-import-01/spool"
log_file = "/var/tmp/lavik-import-01/shake.log"
status_port = 18080
log_interval = 1
rdb_restore_command_behavior = "panic"
target_redis_proto_max_bulk_len = 512000000- 开始前用
CLUSTER NODES和CLUSTER SLOTS盘点源主节点、槽位及通告地址。从迁移主机验证每个源主节点和目标 Owner 的认证及可达性;仅种子可达还不够。 - 迁移窗口内不要进行重新分片、源故障转移或拓扑变更。源与目标的分片数量可以不同,但必须独立验证每个源主节点和每个目标 Group。
- 按目标 Group 明确规划 Function 库和无键命令。合并多个源主节点可能遇到库定义冲突或 FLUSH 操作作用域差异;分片数量相同也不能解决这些问题。
5. 开始导入并同步实时写入
: "${SHAKE_BIN:?Set the absolute path to the pinned redis-shake binary}"
: "${MIGRATION_DIR:?Set the absolute directory for this attempt}"
cd "$MIGRATION_DIR"
# Leave this terminal running through baseline import and incremental catch-up.
"$SHAKE_BIN" "$MIGRATION_DIR/shake.toml"保留此终端运行。在第二个终端检查状态并查看配置中的 shake.log;使用 df -h /var/tmp/lavik-import-01 检查缓冲所在磁盘空间。认证、不支持的类型、已有键或命令失败等错误需要停止并调查本次尝试。进程仍在运行不代表导入成功。
# Run in a second terminal on the migration host.
curl --fail --silent --show-error http://127.0.0.1:18080/status \
| python3 -m json.tool| 配置项 | 作用 |
|---|---|
prefer_replica = false | 直接读取各主节点,最终偏移与对应主节点一致。 |
off_reply = false | 接收目标回复,不要关闭错误及队列核算。 |
rdb_restore_command_behavior = "panic" | 遇到全量恢复键冲突时停止。改为 rewrite 或 skip 不会让部分完成的迁移变得可安全续跑。 |
target_redis_proto_max_bulk_len = 512000000 | 保留本版本已验证的 RESTORE 路径。超过阈值的大值可能转为逐命令重建;Streams 必须保留 RESTORE 路径以维护消费者组及待处理条目元数据。 |
6. 暂停源写入并确认追平
停止全部源写入,包括任务、管理脚本及会改变消费者组状态的 Stream 消费者。保持 Redis 可用于复制和验证。为每个主节点记录以下信息;Cluster 应直连各主节点地址,不要反复只查询种子。
# First stop ALL application, job and administrative writers on Redis.
# Repeat for every source primary (7000, 7001, 7002, ... for a Cluster).
# --askpass avoids putting a password in shell history or process arguments.
# For an ACL user, also add: --user migration-reader
redis-cli -h 127.0.0.1 -p 6379 --askpass INFO server
redis-cli -h 127.0.0.1 -p 6379 --askpass INFO replication
# Record run_id, role:master, master_replid and master_repl_offset per primary.- 将每个 RedisShake reader 与源地址对应。每个 reader 均须为
syncing aof,且aof_received_offset和aof_sent_offset都达到或超过该主节点记录的master_repl_offset。 - 每个目标 writer 均须满足
unanswered_entries = 0和unanswered_bytes = 0。仅一个 reader 追平、RDB 工作归零或consistent = true都不足以决定切换。 - 在验证窗口持续复核源身份和目标所有权。源主节点变更、重启或目标 Owner 切换都会使这次证明失效;应停止本次尝试并按下方恢复流程处理。
7. 验证每个 Group、副本和数据类型
队列排空后,分别连接当前 Owner,在同一连接上执行私有标记的写入、删除及 WAIT,确认副本进度。新建连接只执行 WAIT,无法证明导入写入已到达副本。选择属于该 Owner 槽位范围的未使用标记键;标记和 WAIT 之间不要发生重定向。
# Connect directly to one CURRENT Lavik Owner. Keep this connection open.
redis-cli -h 127.0.0.1 -p 6371 --askpass
# Enter these commands at that interactive prompt, not in separate invocations:
# SET __lavik_migration__:{group1-probe}:barrier ready NX
# DEL __lavik_migration__:{group1-probe}:barrier
# WAIT 1 5000
# Require SET -> OK, DEL -> 1 and WAIT -> at least 1 for a one-replica Group.
# Choose a private, unused marker key whose slot belongs to THIS Owner.
# Repeat for each Group; adjust WAIT's count to its required replica count.若 SET 未返回 OK,应停止,并在删除任何键之前选择另一未使用键。若 WAIT 返回的副本数少于要求,应继续禁止应用访问目标并排查。这是逐 Group 的进度检查,不是全局提交屏障,也不保证异步故障转移时绝不丢失已确认写入。
对每个数据库及每个主节点/Group 完整遍历 SCAN 游标,比较源与目标的完整键集合,同时检查缺失键及多余键。还应读取每个预期目标副本。直连 Cluster 副本时,在同一连接中先执行 READONLY 再读取。键数量和少量 GET 抽样可辅助诊断,但不足以作为最终验证。
| 数据 | 在源与目标上比较 |
|---|---|
| String 与二进制值 | TYPE 和逐字节一致的 GET 值;校验器需保留二进制键和值。 |
| Hash、List、Set、Sorted Set | 用 HSCAN 或 HGETALL 比较全部字段和值,LRANGE 比较列表顺序,SSCAN 比较完整集合成员,ZSCAN 或 ZRANGE WITHSCORES 比较成员及分数。遍历全部游标;无序集合比较不应依赖返回顺序。 |
| 过期时间 | 比较 PEXPIRETIME,包括永久键(-1)和已过期键(-2),并约定绝对过期时间容差。RDB 恢复排队可能延长 TTL;应用要求精确过期时,应在切换前校正截止时间。 |
| Streams | 用分页 XRANGE 比较条目及 ID,并比较 XINFO STREAM/GROUPS/CONSUMERS 及完整 XPENDING 条目(消费者、ID、投递次数)。Redis 7.2 传播可能遗漏 entries-read 诊断计数;如业务依赖该值,应明确校正。 |
| Function 库 | 对每个映射后的目标 Group 及其副本比较 FUNCTION LIST WITHCODE,并演练实际 FCALL 调用。 |
8. 停止 RedisShake 并切换应用
- 偏移、队列、副本及数据检查全部通过,且身份稳定后,在原终端用 Ctrl+C 停止 RedisShake。允许应用流量前确认进程已退出,且没有其他导入进程仍在写入。继续保持源暂停写入。
- 更新应用端点与凭据。按目标使用正常的 Lavik Single/Sentinel 或 Redis Cluster 客户端发现方式;Cluster 客户端必须能发现所有通告节点。重建现有连接池,并仅恢复向 Lavik 写入。
- 运行应用真实的读写路径、事务、Lua/Function 调用及 Stream 消费者。监控错误、延迟、复制健康与容量。在约定回滚窗口保留停止写入的源及备份。
- 在 Lavik 接受新写入前,可将端点切回冻结的源。Lavik 开始接受新写入后,回切必须协调这些新增写入;本流程不提供自动反向复制。
迁移失败时如何处理
本 v4.6.2 流程不能假定具备可靠的断点续传。丢失回复的写入可能已提交;重放状态不明的 INCR、列表追加等非幂等增量会改变结果。RESTORE REPLACE 不能使整个迁移幂等。
- 发生进程退出、连接丢失、目标 Owner 切换或验证失败时,停止导入进程。继续隔离目标应用流量,让 Meta 完成目标恢复,并保留日志供排查。
- 优先使用新的隔离目标及新的迁移/缓冲目录。确保旧导入进程全部退出,且在途命令已排空。保留 sync_rdb 和 sync_aof,从完整基线重新导入,再重复全部切换检查。重建新迁移期间可以恢复源写入。
- 复用隔离的导入目标,需要在全部写入进程停止后,明确清理每个当前 Owner 的键和 Function 库,并确认副本也为空。不要为了修复导入而清空正在服务的生产数据库。
RedisShake 转发写入时不保留 MULTI/EXEC 事务边界,也不会创建跨分片的全局快照。在暂停源写入后的完整比较通过前,不要让部分导入的目标对外服务。