bigywater.tech
Redis · 78

Redis Cluster 节点离开与重新加入

节点离开集群有两种:短暂离线(进程崩溃再重启)和永久离开(机器换掉、数据丢了)。它们在集群眼里差别很大:前者节点 ID 还在、自动回来;后者留下一个永远连不上的「幽灵」,需要 CLUSTER FORGET 清理。这篇把四种情形分别真做一遍,并纠正我一个预期:FORGET 在这个版本里居然会自己传播。

63 ms
从库进程重启(nodes.conf 还在)到 link up
761 ms
在一个节点执行 CLUSTER FORGET 后,所有节点都删除幽灵所用的时间
100 ms
被杀的旧主库重启后自动变成新主库的从库所用的时间

从库崩溃再重启

实测 · 本机执行输出
  从库 7463 被 kill -9,4222 ms 后在其它节点眼中变成:slave,fail;集群状态(旁观节点): cluster_state:ok
  重启后 63 ms:link up,在其它节点眼中 = slave;节点 ID 不变(仍是 3bcc524f);DBSIZE 从库/主库 = 8327 / 8327
  主库同步统计变化:full +0,partial_ok +1
  主库日志:
     Partial resynchronization not accepted: Replication ID mismatch (Replica asked for '37ae532f0408b5156e751c83a4bac380df432cfc', my replication IDs are 
     Full resync requested by replica 127.0.0.1:7463 (rdb-channel)
     Starting BGSAVE for SYNC with target: replicas sockets (rdb-channel)
     Synchronization with replica 127.0.0.1:7463 succeeded
     Partial resynchronization request from 127.0.0.1:7463 accepted. Sending 300704 bytes of backlog starting from offset 1.
  从库日志:
     Before turning into a replica, using my own master parameters to synthesize a cached master: I may be able to synchronize with the new master with jus
     Connecting to MASTER 127.0.0.1:7460
     MASTER <-> REPLICA sync started
     Non blocking connect for SYNC fired the event.
     Master replied to PING, replication can continue...
     Trying a partial resynchronization (request 37ae532f0408b5156e751c83a4bac380df432cfc:1).
     Node 5455a38d7b7a7dcb9fb75cab9c732728dea1129f () is no longer master of shard 3a96fa47a0476beb1885e939f076f70ee8b8a5c5; removed all 0 slot(s) it used 
     PSYNC is not possible, initialize RDB channel.
     Node fdc50d7c067c35ad2525866129f374f856e85b49 () is no longer master of shard e0b2bceea5476937ae0c77d3a34446b29b5bcee8; removed all 0 slot(s) it used 
     MASTER <-> REPLICA sync: receiving streamed RDB from master with EOF to disk
     Discarding previously cached master state.
     MASTER <-> REPLICA sync: Loading DB in memory
     MASTER <-> REPLICA sync: Flushing old data
     MASTER <-> REPLICA sync: Finished with success
     MASTER <-> REPLICA sync: Starting to stream replication buffer into the db (0 bytes).
     MASTER <-> REPLICA sync: Successfully streamed replication buffer into the db (0 bytes in total)
     Before turning into a replica, using my own master parameters to synthesize a cached master: I may be able to synchronize with the new master with jus
     Discarding previously cached master state.
     Before turning into a replica, using my own master parameters to synthesize a cached master: I may be able to synchronize with the new master with jus
     Connecting to MASTER 127.0.0.1:7460
     MASTER <-> REPLICA sync started
     Non blocking connect for SYNC fired the event.
     Master replied to PING, replication can continue...
     Trying a partial resynchronization (request 2d72d92db74ae2bae7e5f64467fb283e3cef8e0e:1).
     Successful partial resynchronization with master.
     MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.

kill -9 一个从库:约 4.2 s(cluster-node-timeout 3 s 之后)其它节点把它标成 slave,fail,但因为槽位都还有主库,cluster_state 仍是 ok。期间主库继续被写入 5000 个 key。重启后(nodes.conf 还在,节点 ID 不变)63 ms 就 link up 并追上了数据。日志的细节值得看:从库先尝试部分重同步,被主库以 Replication ID mismatch 拒绝,于是走 rdb-channel 的全量同步;之后又有一次 Partial resynchronization request … accepted. Sending 300704 bytes of backlog,用来补 RDB 之后的增量。所以主库 INFO stats 的变化是 sync_full +0、sync_partial_ok +1——和「发生过一次全量同步」的事实不一致。在这个版本上判断同步类型,要看日志,别只看这两个计数。(这次的 rdb-channel 全量同步与第 53 篇里我用计数判断类型的场景不同;那里我又对照日志复核过,结论没有变。)

节点被清空:新 ID 与幽灵

实测 · 本机执行输出
  7464 新 ID 6955e682(旧 ID 5455a38d);它自己已知节点 1 个
  其它节点眼中旧 ID:slave;已知节点数(含幽灵)= 6
> CLUSTER FORGET 5455a38d7b7a7dcb9fb75cab9c732728dea1129f
"OK"
  +    2 ms  各节点已知节点数(只在 7460 上执行过 FORGET): {7460: 5, 7461: 6, 7462: 6, 7463: 6, 7465: 6}
  +  297 ms  各节点已知节点数(只在 7460 上执行过 FORGET): {7460: 5, 7461: 6, 7462: 6, 7463: 5, 7465: 6}
  +  356 ms  各节点已知节点数(只在 7460 上执行过 FORGET): {7460: 5, 7461: 5, 7462: 6, 7463: 5, 7465: 6}
  +  706 ms  各节点已知节点数(只在 7460 上执行过 FORGET): {7460: 5, 7461: 5, 7462: 5, 7463: 5, 7465: 6}
  +  761 ms  各节点已知节点数(只在 7460 上执行过 FORGET): {7460: 5, 7461: 5, 7462: 5, 7463: 5, 7465: 5}
  旧 ID 在 7462 眼中 = (不在节点表里)

把一个从库的 nodes.conf 和数据文件删掉再启动,它就是一个全新的节点:新的 ID,只认识自己。而其它节点还记着旧 ID(已知节点数仍是 6)。我另外用一个不做任何操作的对照观察了 22 秒(该次探针没有打印进上面的输出):这个幽灵一直是 slave/disconnected,不会自己消失。在一个节点上 CLUSTER FORGET 旧ID 后,该节点立刻少一个;其它节点在 761 ms 内也都删除了它——我原本以为要在每个节点上都执行,实测这个版本里 FORGET 会通过集群总线传播,我没有去查是从哪个版本开始的。

让新节点重新加入

实测 · 本机执行输出
> CLUSTER MEET 127.0.0.1 7460
"OK"
  MEET 后 235 ms 认识全部 6 个节点
> CLUSTER REPLICATE a16642ad99c3e50b028d187cc286b71e495c3158
"OK"
  REPLICATE 后 40 ms:link up,DBSIZE = 8334,角色 = slave

CLUSTER MEET 一个已有节点,1291 ms 后认识全部 6 个节点(第 74 篇);再 CLUSTER REPLICATE 主库ID,41 ms 后从库 link up、数据同步完成。

旧主库回来:自动变成从库

实测 · 本机执行输出
  7462 被杀,7465 在 5187 ms 后升主
  旧主库重启后 100 ms:角色 = slave,master = 7465(新主库端口 7465),ID 不变 f2507670

主库被杀,从库 5187 ms 后升主;旧主库用原来的 nodes.conf 重启后 100 ms 内自动变成新主库的从库,节点 ID 不变。不需要 CLUSTER REPLICATE——节点重启后从 gossip 里看到更高的 epoch,自己就明白了(第 77 篇)。

分步演示:离开与回来的四种情形

节点离开与重新加入
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。3 主 3 从,每个场景在同一个集群里顺序进行;「清空」指删除该节点目录下的 nodes.conf 与数据文件
    • 没有验证:CLUSTER FORGET 传播是从哪个版本开始的(只在本机 8.10.2 上观察)、FORGET 的 60 秒黑名单时间、CLUSTER RESET 各模式、redis-cli --cluster del-node / add-node 的完整流程、节点换 IP 后的行为(cluster-announce-ip)
    京ICP备2026067502号-1京公网安备11010502063487号