早期的 Redis 复制里,只要主从关系一变——比如故障转移——所有从库都要做一次全量同步。PSYNC2 让节点记住「我上一任主库的复制 ID 和 offset」,换主之后可以接着同步。这篇用三个真实节点、一次真实的提升,看这些 ID 和 offset 怎么变,并对照哪些场景仍然要全量同步。
A: role=master replid=6a40066f… replid2=0… offset= 31803 second_offset=-1 B: role=slave replid=6a40066f… replid2=0… offset= 31803 second_offset=-1 C: role=slave replid=6a40066f… replid2=0… offset= 31803 second_offset=-1 A(主): sync_full=2 sync_partial_ok=0
master_replid 是一段复制历史的标识,master_repl_offset 是这段历史里已复制的字节位置。主库和它的从库共享同一个 replid,offset 也一致;A 上的 sync_full=2 是 B、C 最初各做了一次全量同步。
A: role=master replid=6a40066f… replid2=0… offset= 31803 second_offset=-1 B: role=master replid=551b2c4d… replid2=6a40066f offset= 31803 second_offset=31804 B 的 replid2 == A 的旧 replid:True;second_repl_offset = 31804(提升时 offset 31803 + 1)
B 执行 REPLICAOF NO ONE 之后:master_replid 换成新的(551b2c4d…),旧的存入 master_replid2,second_repl_offset = 31804,正好是提升时 offset(31803)加 1。两个字段合起来的含义是:用旧 replid 来请求、并且请求的起始位置不超过 second_repl_offset 的从库,可以接受(下一步日志里 C 请求的起始位置就是 31804,正好等于 second_repl_offset)。
B(新主): sync_full=0 sync_partial_ok=1 (变化: full +0, partial_ok +1) B 的日志: Partial resynchronization not possible (no cached master) Partial resynchronization request from 127.0.0.1:7154 accepted. Sending 0 bytes of backlog starting from offset 31804. C: role=slave replid=551b2c4d… replid2=6a40066f offset= 31803 second_offset=31804
C 带着旧 replid 和自己的 offset 向 B 发 PSYNC,B 的日志是 Partial resynchronization request from 127.0.0.1:7154 accepted. Sending 0 bytes of backlog starting from offset 31804:接受,需要补的数据是 0 字节。B 的 sync_full 增量 0、sync_partial_ok 增量 1。日志里还有一行 Partial resynchronization not possible (no cached master),那是 B 自己(刚被提升、没有缓存的主库)的一条记录,不是对 C 的拒绝。
A 重新加入之后 B: full +0, partial_ok +1 dbsize A/B/C: 1500 1500 1500 A: role=slave replid=551b2c4d… replid2=6a40066f offset= 47606 second_offset=31804 B: role=master replid=551b2c4d… replid2=6a40066f offset= 47606 second_offset=31804 C: role=slave replid=551b2c4d… replid2=6a40066f offset= 47606 second_offset=31804
B 上又写了 500 个 key;旧主 A 用 REPLICAOF 挂到 B:仍然是部分重同步(full +0、partial_ok +1),三个节点 dbsize 都是 1500,replid 全部收敛为新主的 551b2c4d…。
B: full +1, partial_ok +0
新节点 D 有自己的复制历史,replid 与 B 的 replid、replid2 都对不上,B 只能全量同步(sync_full +1)。所以「换主可以部分重同步」有前提:两边的历史在某个 offset 之前是同一条。