从 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 SinglefalsefalseDB0–15
Redis → Lavik ClusterfalsetrueDB0;多键操作必须同槽
Redis Cluster → Lavik SingletruefalseDB0
Redis Cluster → Lavik ClustertruetrueDB0;多键操作必须同槽

这些开关描述两端的客户端协议,而非机器数量。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;安排迁移前先确认可用性。整个迁移期间保持源拓扑稳定。

部署托管 Single →

部署主从集群 →

以下回环示例使用 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。

Linux · bash
# 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"

为本次迁移创建私有目录。如果选择其他目录或磁盘,请同时更新配置中的两个绝对路径。失败后不要复用旧缓冲目录。

Linux · bash
# 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,同时完成全量导入与增量追平。

Linux · bash
[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。不要拼接两份配置,也不要同时将两者运行在同一目标上。

Linux · bash
[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. 开始导入并同步实时写入

Linux · bash
: "${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 检查缓冲所在磁盘空间。认证、不支持的类型、已有键或命令失败等错误需要停止并调查本次尝试。进程仍在运行不代表导入成功。

Linux · bash
# 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 应直连各主节点地址,不要反复只查询种子。

Linux · bash
# 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 之间不要发生重定向。

Linux · bash
# 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 事务边界,也不会创建跨分片的全局快照。在暂停源写入后的完整比较通过前,不要让部分导入的目标对外服务。

Lavik 0.2.0 Redis 导入详细说明 →