Redis Cluster无缝迁移实战:节点置换与redis-shake双方案详解
发布时间:2026/10/8 3:54:18 锦皓数字建站

做了这么多年 Redis 运维我越来越觉得集群迁移最考验人的不是命令本身而是对无缝这两个字的理解。今年年初刚完成一次从三节点到三节点的 Redis Cluster 迁移旧集群三个 master新集群也是三个 master业务侧全程无感没出现过一次 CLUSTERDOWN也没有一条写失败。今天把整套方案完整写出来包括适用场景、完整命令、排障经验和一张可以直接拿去用的检查清单。主线有两条一条是在同一个集群内做节点置换把旧节点一个个请出去真正做到业务无感另一条是用 redis-shake 做全量加增量同步适合跨机房、跨版本、必须保留旧集群回退的场景。两条路线我都跑过多次下面的坑基本也全踩了一遍。1. 迁移前先盘清楚这三件事1.1 三节点的 Redis Cluster 到底长什么样很多人一听说三节点就下意识觉得是主从加哨兵其实 Redis Cluster 的三节点通常就是三个 master16384 个槽位三等分每个节点管自己那一段key 通过 CRC16 计算落到哪个槽槽归哪个节点管。Redis 的数据类型说白了就是 string、hash、list、set、zset 这五件套加各种扩展结构迁移时不同类型 key 的处理难度完全不同一个大 hash 和一万个小 string 的迁移耗时和风险不是一个量级。如果你们环境是高可用配置三节点往往只是三个 master背后还可能挂三个 replica那就是六台机器的规模。标题里说的三节点到三节点我把两种情况都覆盖到最小三节点怎么迁带副本的六节点又该怎么处理。核心思路一致只是收尾阶段多一步副本处理。还有一个基础概念必须说清楚Redis Cluster 节点之间除了业务端口还有一个集群总线端口固定是业务端口加 10000。节点之间靠这个端口走 gossip 协议交换拓扑信息新节点入群、槽位变更、节点掉线全部靠它传播。迁移过程中任何防火墙策略如果只放行了 6379 而漏掉 16379后面一定会出节点被踢的怪问题这个后面排障部分会细说。1.2 迁移前必须记录的存量数据我见过太多人一上来就开始操作迁移完不知道到底丢没丢数据因为压根没有基线。动手之前先把下面的信息逐条记录迁移完成后逐项对比这就是最硬核的验收数据。检查项命令记录什么集群状态redis-cli -a 密码 cluster infocluster_state 是否为 okslot 分配是否完整节点拓扑redis-cli -a 密码 cluster nodes每个节点的 node id、角色、槽位区间槽位分布redis-cli -a 密码 cluster slots槽位到节点的映射关系数据量redis-cli -a 密码 --cluster check 任意节点:6379每个节点的 key 数量、总槽位数内存基线redis-cli -a 密码 info memoryused_memory、maxmemory、内存碎片率大 key 清单redis-cli -a 密码 --bigkeys超过阈值的大 key 列表客户端规模redis-cli -a 密码 client listconnected_clients、阻塞连接慢日志redis-cli -a 密码 slowlog get 50迁移前是否有慢命令基线版本信息redis-cli -a 密码 info serverredis_version新旧版本兼容性判断这里有个容易被忽略的细节--bigkeys默认只按类型统计最大的几个 key但它扫描过程本身会对线上实例产生一定压力建议在低峰期跑并且加-i 0.1之类的参数控制每个 key 之间的休眠时间。迁移前做一次大 key 摸底不是为了好看而是为了判断后面 reshard 搬槽位时会不会卡死这个关联后面专门讲。1.3 三条路线和选型标准三节点迁三节点方案上其实就那么几条但选型错了后面全是坑。我按实际项目里的取舍逻辑排了个优先级。方案基本原理停机窗口适用场景A. 集群内节点置换新节点加入旧集群槽位逐步搬迁旧节点退群理论零停机同机房、同网络、版本兼容B. redis-shake 同步新集群独立部署通过 psync 全量加增量同步秒级到分钟级切换窗口跨机房、跨版本、需要旧集群回退C. RDB/AOF 备份恢复停写、备份、恢复、切流量分钟级数据量小、允许维护窗口选型逻辑其实一句话就能讲透能在一个集群内解决的问题绝不要引入第二个集群。方案 A 的迁移过程完全在 Redis Cluster 自己的协议体系内完成槽位搬迁是原子操作客户端能感知的就是正常重定向不需要任何外部工具。方案 B 是当你没办法用方案 A 时才上的比如两个机房网络不通、Redis 版本跨度大到无法互相 gossip、或者业务明确要求旧集群保留一段时间作为回退。方案 C 我基本不推荐除非数据量在几 GB 以内且业务能接受停写十分钟以上否则备份恢复的坑比另外两种多得多——RDB 加载本身就有内存膨胀问题AOF 重放在大写入量下还会直接拖垮恢复速度。还有一个原则不管选哪条路迁移成功标准必须在动手前定死。一般我会定三个指标写成功率 100%、数据零丢失、切换后 p99 延迟比迁移前不劣化超过 5ms。没有这三个数后面出问题你根本说不清到底是迁移害的还是业务本身抖动。2. 方案一集群内节点置换真正做到业务无感2.1 为什么它能无缝槽位迁移与 Gossip 协议方案 A 能无缝的核心原因是 Redis Cluster 把数据拆成了 16384 个槽槽是最小的数据所有权单位。新节点入群后你用工具把旧节点的槽一个个搬给新节点搬的过程就是针对槽里的每个 key 执行 MIGRATE。MIGRATE 是原子操作把 key 序列化发给目标节点目标节点收到并写入源节点确认成功后才删除本地副本整个过程带 TTL 一起搬。对客户端来说这个 key 在搬走的瞬间会收到一个 ASK 重定向懂 Cluster 协议的客户端会自动去新节点拿数据业务逻辑完全无感知。Gossip 协议负责把槽归谁管这个拓扑信息扩散到所有节点和客户端。每次搬迁完一个槽集群里所有节点通过集群总线端口10000互相广播变更客户端定期从任意节点拉取 cluster slots 刷新本地拓扑缓存。这套机制跑得稳整个迁移就不存在需要停服务的瞬间。但有个配置必须提前改cluster-require-full-coverage。默认是 yes表示只要有一个槽没被覆盖整个集群对外拒绝服务。迁移搬槽位的过程中虽然设计上不会出现槽空档但节点抖动、网络分区这种意外谁也不敢保证所以强烈建议在生产环境把它改成 no代价是极端情况下未覆盖的槽对应的 key 会报错但不会拖垮整个集群。2.2 动手前新节点的配置和入群新节点在加入集群之前先按生产标准把配置准备好。核心配置项就这么几个cluster-enabled yes、cluster-node-timeout 15000、appendonly yes、maxmemory和旧集群保持一致如果开了密码认证requirepass和masterauth都要配否则后面做主从同步或者集群内部通信会验证失败。想先在本地演练一遍的用 Docker 起三个节点是最省事的命令也很直观docker run -d --name redis-node1 --network host \ -v /data/redis/node1:/data \ redis:7.0 redis-server \ --port 6379 --cluster-enabled yes --appendonly yes --cluster-config-file nodes.conf注意这里用了--network host是为了让三个容器能通过集群总线互相访问省去容器网络的端口映射麻烦。Windows 用户别在原生 Redis 里折腾集群模式老版本支持很别扭直接上 WSL 或者 Docker Desktop。真到了生产环境新节点机器上只需准备好 redis.conf、数据目录、systemd 或脚本托管然后执行加入命令redis-cli -a 密码 --cluster add-node 10.0.2.11:6379 10.0.1.11:6379参数含义第一个地址是待加入的新节点第二个地址是旧集群里任意一个在线节点。执行成功后新节点会成为没有任何槽位的 master。马上用cluster nodes确认它的 node id 并记下来后面搬槽全靠这个 id 指路。2.3 核心用 reshard 把槽位从旧节点搬到新节点这一步是整个迁移的心脏。先取到旧节点和新节点的 node id然后执行redis-cli -a 密码 --cluster reshard 10.0.1.11:6379 \ --cluster-from 旧节点ID \ --cluster-to 新节点ID \ --cluster-slots 5461 \ --cluster-yes这里--cluster-slots 5461是把旧节点管理的全部槽位一次搬完。默认三节点均分时每个节点 5461 或 5462 个槽搬完一个节点再搬下一个。如果数据量大我更建议把 5461 拆成 1000 槽一批分五次搬。原因有两个一是单批搬完耗时太长过程中出了幺蛾子不好回退二是分批搬可以每搬完一批看一次新节点的内存和 key 数量心里有底。搬槽过程中redis-cli 会一行行打印Moving slot xxx from 旧ID to 新ID后面跟一个点表示进度。这个过程中源节点会对每个 key 执行序列化和网络传输目标节点边收边写两边内存都会阶段性上涨网络带宽也会有明显占用。所以这条命令最好放在低峰期并且提前看一下目标节点的内存余量至少留出源节点数据量 1.2 倍的空间。搬完一个旧节点后用cluster nodes确认旧节点的槽位区间已经归零新节点的槽位区间已经补上。然后把另外两个旧节点按同样方式搬到对应的两个新节点。全部搬完后用--cluster check确认 16384 个槽全部处于 covered 状态没有槽丢失或双归属。2.4 旧节点退群与现场清理确认槽位全部搬干净后旧节点现在已经是一个没有数据的空 master还赖在集群里占着位置。把它请出去redis-cli -a 密码 --cluster del-node 10.0.1.11:6379 旧节点ID如果旧节点上还残留任何槽位这条命令会直接报ERR Cant forget node that has slots意思很明确没搬干净别想退。del-node 执行成功后集群里所有其他节点都会对这个旧节点执行 cluster forget把它从拓扑里抹掉。但这还没完旧节点自己还保留着集群配置如果不处理它重启后可能会尝试重新加入集群造成未知冲突。所以要登到旧机器上执行redis-cli -a 密码 cluster reset hard然后删掉或者改名它的 cluster-config-file默认是 nodes.conf再停进程。到这一步三个旧节点全部清理完毕集群从拓扑上看就是三个新节点组成的三主集群和迁移前一模一样但硬件和底层环境已经是新的。这里有一个特别容易犯的错误我必须单独拎出来说客户端连接配置里如果写死了旧节点的 IP旧节点退群后客户端拿到的是过期的拓扑缓存会出现间歇性的连接失败和MOVED重定向错乱。所以退群前务必确保客户端 seed 列表里至少有一个新节点的地址或者直接用 DNS/VIP 指向集群节点让拓扑刷新能落到活着的节点上。这也是无缝很容易被忽视的最后一公里。2.5 变体新节点先当副本用 failover 完成置换直接搬槽位适合数据量中等、能接受较长搬迁时间的场景。如果集群里有几百 GB 甚至上 TB 数据逐 key MIGRATE 会把人熬疯这时候用另一个变体让新节点先以副本身份加入全量复制完成后做一次计划内 failover把主从角色换过来。步骤也不复杂。第一步redis-cli -a 密码 --cluster add-node 10.0.2.11:6379 10.0.1.11:6379 \ --cluster-slave --cluster-master-id 旧节点ID这样新节点会作为旧节点的 replica 加入立刻开始全量同步数据通过 RDB 一次性拉过来比逐 key 搬迁快得多。等到info replication里看到master_link_status:up且主从 offset 完全追平就可以执行角色切换。连接上这个新节点当前是 replica执行redis-cli -p 6379 -a 密码 cluster failover这是优雅 failover副本会请求主节点暂停接收写请求同步完最后差量然后把自己提升为 master。整个切换过程只有几十毫秒到一两秒的窗口期间客户端可能会收到少量重定向但对大多数业务来说属于无感级别。切换完成后新节点变成 master旧节点降级成它的 replica。接下来重复这个过程把另外两对也换掉最后三个旧节点都是 replica直接 del-node 摘掉即可。这个变体的优势是复制阶段数据一致性好、速度快代价是每对节点都要经过一次 failover对极端敏感的业务会产生极小的毛刺。搬槽位和 failover 哪个更适合你主要看两个权衡数据量以及业务对毫秒级抖动的容忍度。3. 方案二跨机房或异构环境用 redis-shake 搭一座桥3.1 什么场景必须上 redis-shake方案一虽然干净利落但有一个硬前提新旧节点必须在同一个网络域并且能通过集群总线正常通信。跨机房、跨 VPC、物理隔离这种环境gossip 的延迟和丢包会直接导致节点被反复判定为下线方案一根本跑不起来。还有一种情况是版本跨度太大比如旧集群是 Redis 3.x新集群是 7.x老节点连广播协议都不兼容也没法加入同一个集群。这时候就要走方案二两边各自独立成团中间用 redis-shake 做数据同步。另外如果你的业务对回滚有硬性要求——比如迁移后要保留旧集群七天随时准备切回去——方案二也是唯一选择。因为方案一的本质是旧节点退群回滚等于要把旧节点重新拉回来步骤繁琐而且很难保证状态干净。而方案二从始至终就是两套独立集群回滚就是把同步方向反过来简单直接。3.2 新集群搭建与 redis-shake 配置先把新集群独立建好这也是一个三节点集群用标准命令即可redis-cli -a 密码 --cluster create \ 10.0.2.11:6379 10.0.2.12:6379 10.0.2.13:6379 \ --cluster-replicas 0如果要带高可用副本就把节点数量加到六个--cluster-replicas 1。然后下载 redis-shake。这里要提醒一句redis-shake 目前有 2.x、3.x、4.x 多个版本配置字段不完全一致最稳妥的做法是下载和你 Redis 版本匹配的 release然后照着 release 自带的 redis-shake.conf 改。网上随便抄一段配置就跑大概率会踩版本字段不兼容的坑。核心配置就这几项type sync source.address 10.0.1.11:6379 source.password old-pass target.address 10.0.2.11:6379 target.password new-pass key_exists rewrite parallel 4 big_key_threshold 52428800type sync表示全量加增量同步模式会先把源端已有的数据全量拉过来然后持续消费增量写入直到你主动停掉。source.address和target.address各填一个节点的地址即可它自己会通过 cluster slots 发现完整的拓扑。key_exists rewrite决定目标端已有同名 key 时怎么处理如果是全新集群改成none更安全出现冲突直接报错提醒你。parallel控制并发数默认不用改得太高数据量大的时候再慢慢调。big_key_threshold表示超过这个字节数的大 key 单独走特殊通道处理避免大 key 阻塞同步主流程。启动命令很简单前台跑起来看日志更直观./redis-shake -conf redis-shake.conf它会先做全量同步日志里能看到大量批量写入的统计全量完成后进程别停它会像一个小 slave 一样持续盯着源端的增量。这时候重点看日志里源和目标之间的差值这个值持续往 0 收敛说明已经追平。3.3 追平增量后的切换与回滚同步追平后新旧集群的数据理论上是一致的但注意是理论上。切换之前我通常会把新集群临时挂到只读状态让一部分读流量先打过去或者在测试环境里对着新集群跑一遍核心链路确认读没有问题。这步相当于灰度验证成本很低但能暴露很多同步阶段发现不了的问题比如数据序列化格式不兼容、某些业务 key 的 TTL 语义变了。真正的写切换业界没有魔法无非三种做法一是短暂停写在业务低峰让网关或者写入口熔断几分钟等 redis-shake 把最后一波增量追平然后把客户端配置全部切到新集群二是在停写前面加一个追平确认确认差值归零再放流量三是用消息队列或者双写中间件做过渡期双写成本高一般业务用不上。我做过的项目基本都是第一种选凌晨低峰停写两到五分钟切换观察放量整个过程业务是能接受的。回滚预案必须有。最朴素的实现就是反方向再跑一个 redis-shake把 source 指向新集群target 指向旧集群。一旦新集群出了问题数据已经反同步回旧集群客户端配置再切回去即可。但要清醒认识到反同步只能保证数据被拉回如果切换期间业务在新集群产生了大量写入回滚后旧集群会丢掉最后那段时间的数据业务要能接受这个回退窗口。还有一个隐藏的坑和分布式锁相关。如果你们的业务在 Redis 上做过分布式锁或者幂等标记迁移期间锁 key 也是被同步的对象。双集群并存阶段一旦业务同时在两边加锁就会出现锁双活分布式锁直接失效严重的会演变成线上事故。所以切换前务必确认写流量只有一个入口老集群不能让业务再写进去这是硬规定。3.4 一致性校验别只信 dbsize同步完成不等于数据没问题我见过不少 dbsize 数字对得上但实际值不一致的案例。校验的组合拳是这样打的。先用redis-cli --cluster check看槽位和 key 分布是否正常这是第一层粗校验。然后逐节点dbsize和info keyspace对比总和。接着上 redis-full-check这是和 redis-shake 同一个团队出的一致性校验工具能输出 key 总数差异、类型不一致、值不一致、TTL 不一致四类统计。注意它对 TTL 的判断有已知误差具体以你下载版本自带的 README 为准遇到 TTL 报错不要慌先人工抽查几个 key 确认。如果你们没有条件用 redis-full-check自己写脚本也行在源端scan一批 key对每个 key 分别type、取值、算哈希去目标端对比同样 key 的哈希。抽样比例不用太高核心业务 key 全覆盖其余 key 抽 5% 到 10% 就够了。抽查对象一定要包含大 key、热点 key 和近一小时有写入的 key这些是最容易在同步中出问题的地方。4. 迁移中最容易翻车的地方4.1 slot 迁移期间的三个经典报错搬槽位阶段最经典的报错是Slot xxx is already busy。这个错误十有八九是新节点之前被加入过别的集群或者 nodes.conf 里残留了历史槽位信息导致它认为某个槽归自己管。解决办法很干脆连上新节点执行cluster reset hard清掉脏配置再重新 add-node。第二个经典报错是退群时报ERR Cant forget node that has slots原因就是槽没搬干净处理方式不是硬删而是老老实实把剩余槽搬走。有个小技巧搬槽之前先用cluster nodes确认这个节点的槽位区间对照着搬就不会出现搬到一半发现还有残留的情况。第三个问题不是报错而是隐患大 key 导致 MIGRATE 超时。Redis 是单线程模型MIGRATE 一个几十 MB 的大 key 时源节点整个实例会被这个序列化加网络传输的过程阻塞住期间所有读写都会卡顿。这就是为什么迁移前要跑--bigkeys摸底。遇到超大 key要么调大--cluster-timeout和 MIGRATE 的超时参数要么在低峰期单独处理总之别让它在高峰期混在大部队里搬。4.2 gossip 拓扑与客户端感知的坑很多人在迁移时会遇到新节点莫名其妙退群的怪现象表现是cluster nodes里新节点一会儿在一会儿不在最后彻底消失。排查方向要直接指向集群总线检查业务端口加 10000 的防火墙和安全组是否放通gossip 通信不通节点之间互相超时超时次数多了就会被集群判定为下线并踢出。客户端的坑更隐蔽。像 Redis Desktop Manager、Another Redis Desktop Manager 这类可视化客户端它们一般会缓存连接节点的拓扑信息迁移期间界面显示的 slot 分布往往是旧的看了反而误导排障。我的习惯是迁移全程只用 redis-cli 检查可视化工具留到迁移结束后再连新集群看数据。另外客户端连接池如果配置的节点列表全是旧 IP退群后会出现周期性报NOADDR或者连接超时直到某个节点把新拓扑返回给它。这一步在切换时最容易引起看起来集群挂了的误报提前在客户端配置或 DNS 层面解决掉。4.3 redis-shake 实战踩过的坑redis-shake 我用下来最常见的三个问题一是目标端已有数据导致冲突解决方法是清空目标集群或者把key_exists改成rewrite但改之前要想清楚被覆盖的旧数据如果是业务需要的那就成了事故二是源端版本太老不支持某些命令或者模块命令redis-shake 无法同步这种只能先做业务改造或者目标端升级三是大 key 导致增量延迟一直拉大。第三个问题要重点盯全量同步阶段redis-shake 会触发源端做 RDB源实例的内存和磁盘 IO 会明显上涨预留足够资源全量结束后进入增量阶段大 key 每来一次写入就要整 key 重传延迟会反复跳所以大 key 一定要提前拆或者单独处理。4.4 大 key、热 key、过期 key 的专项处理大 key 的破坏力前面说了这里给一个更细的处理思路大 hash、大 set、大 zset 这类集合型大 key可以在迁移前用源端的hscan、sscan、zscan分页读出数据分批写入目标端最后删掉源端大 key。这样做虽然操作复杂但比直接 MIGRATE 一个巨型 key 安全得多。热 key 的问题在于迁移过程中它还在被高频读写。如果这个 key 刚好在搬迁的那一批里MIGRATE 期间源端会短暂阻塞这个 key 的访问遇到极端热点还会引发连锁超时。没有特别好的办法只能把热 key 的迁移放到业务最低峰或者临时在业务侧对该 key 做降级处理。过期 key 是校验阶段最容易误判的部分。同步工具会把 TTL 一起搬过去但 full-check 对 TTL 的比较经常报差异很多时候是因为源端 key 在扫描间隙刚好过期。遇到 TTL 不一致的报告先人工抽查别急着定位为同步故障。5. 我的迁移检查清单和一点收尾建议5.1 一张直接能用的检查清单以下是我每次迁移都会过一遍的清单从准备到收尾四个阶段照着勾就行。准备阶段确认新旧集群版本兼容性记录旧集群版本、内存、key 数、big key 清单。确认迁移成功标准写成功率、数据一致性、延迟劣化阈值。低峰窗口确认业务侧和运维侧拉齐通知渠道。新集群内存、CPU、网络带宽资源确认足够。防火墙放通业务端口和集群总线端口。执行阶段记录所有节点 node id对照槽位区间规划搬迁顺序。每次搬槽后检查cluster nodes确认槽位归属正确。同步过程中盯源和目标的内存、带宽、延迟指标。redis-shake 场景下确认增量差值归零才切换。切换阶段客户端 seed 列表至少保留一个新节点地址或 DNS/VIP 已指向新集群。切换后逐节点对比 dbsize 和 key 抽样哈希。观察业务错误率和写成功率出现异常立即回滚。收尾阶段旧节点执行cluster reset hard并清理 nodes.conf。更新监控告警、配置文件、架构文档中的节点地址。关闭或调整 redis-shake 同步进程。观察至少 72 小时再宣布迁移完成。5.2 迁移后的监控、备份与文档沉淀迁移完成不是终点新集群接管了流量但它本质上还是一件新上线的系统。接下来三天内我会盯这几个指标集群状态是否稳定为 okreplica 延迟是否归零内存碎片率和逐出键数量是否正常以及客户端重定向频率有没有异常。这些指标任何一个持续异常都说明迁移留下了尾巴比如槽位分布不合理、客户端配置残留、或者是 maxmemory 设置不当导致淘汰策略开始误伤。备份策略也是迁移后首日就要做的新基线。新集群确认稳定后重新配置 RDB/AOF 的持久化计划做一次全量备份并把备份产物纳入原有的恢复演练体系。很多人迁移完第一周是裸奔状态等到出问题才发现新集群连备份都没有这种教训不值得再犯。最后再聊一点我自己的感受。做了这么多年 Redis 运维我越来越觉得迁移方案不管写得多漂亮都必须在预发环境完整演过一遍才算数。任何没有演过的方案都只能叫赌注因为 Redis Cluster 的坑从来不在概念里而在真实网络的延迟、真实数据的分布、真实业务的访问模式里。你把这套命令原样拿到预发跑一遍哪怕数据量只有生产的一成也能逼出七八成的问题。迁移这东西谨慎不是胆小是对自己手里的线上系统负责。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。