欢迎光临
我们一直在努力

ZooKeeper 数据同步机制深度解析:Leader 选举后的数据对齐

ZooKeeper 数据同步机制深度解析:Leader 选举后的数据对齐

    • 一、数据同步概述
      • 1.1 为什么需要数据同步?
      • 1.2 数据同步的核心目标
    • 二、同步前的准备工作
      • 2.1 Follower 连接 Leader
      • 2.2 关键数据结构
    • 三、四种同步策略详解
      • 3.1 DIFF 同步(差异化同步)
      • 3.2 TRUNC 同步(截断同步)
      • 3.3 SNAP 同步(全量快照同步)
      • 3.4 TRUNC+DIFF 同步(复合同步)
    • 四、同步完成确认
    • 五、完整数据同步示例
      • 5.1 场景一:3节点集群数据同步
      • 5.2 命令行验证
    • 六、数据同步的源码分析
      • 6.1 Leader 端决策逻辑
      • 6.2 Follower 端处理逻辑
    • 七、总结
      • 7.1 四种同步策略对比
      • 7.2 数据同步完整流程图
      • 7.3 一句话总结

🌺The Begin🌺点点关注,收藏不迷路🌺

摘要:Leader 选举完成后,新当选的 Leader 必须与集群中所有 Follower 进行数据同步,确保每个节点的数据视图一致。这是 ZooKeeper 保证数据一致性的关键步骤。本文将深入剖析 Leader 选举后的数据同步机制,详细讲解四种同步策略(DIFF、TRUNC、SNAP)的触发条件和执行流程,通过源码分析和实战示例,帮助读者全面理解这一核心机制。

一、数据同步概述

1.1 为什么需要数据同步?

Leader 选举完成后,集群中各个节点的数据状态可能不一致:

#mermaid-svg-IPH3vLY1EBE3k6EW{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IPH3vLY1EBE3k6EW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IPH3vLY1EBE3k6EW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IPH3vLY1EBE3k6EW .error-icon{fill:#552222;}#mermaid-svg-IPH3vLY1EBE3k6EW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IPH3vLY1EBE3k6EW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IPH3vLY1EBE3k6EW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IPH3vLY1EBE3k6EW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IPH3vLY1EBE3k6EW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IPH3vLY1EBE3k6EW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IPH3vLY1EBE3k6EW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IPH3vLY1EBE3k6EW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IPH3vLY1EBE3k6EW .marker.cross{stroke:#333333;}#mermaid-svg-IPH3vLY1EBE3k6EW svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IPH3vLY1EBE3k6EW p{margin:0;}#mermaid-svg-IPH3vLY1EBE3k6EW .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-IPH3vLY1EBE3k6EW .cluster-label text{fill:#333;}#mermaid-svg-IPH3vLY1EBE3k6EW .cluster-label span{color:#333;}#mermaid-svg-IPH3vLY1EBE3k6EW .cluster-label span p{background-color:transparent;}#mermaid-svg-IPH3vLY1EBE3k6EW .label text,#mermaid-svg-IPH3vLY1EBE3k6EW span{fill:#333;color:#333;}#mermaid-svg-IPH3vLY1EBE3k6EW .node rect,#mermaid-svg-IPH3vLY1EBE3k6EW .node circle,#mermaid-svg-IPH3vLY1EBE3k6EW .node ellipse,#mermaid-svg-IPH3vLY1EBE3k6EW .node polygon,#mermaid-svg-IPH3vLY1EBE3k6EW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IPH3vLY1EBE3k6EW .rough-node .label text,#mermaid-svg-IPH3vLY1EBE3k6EW .node .label text,#mermaid-svg-IPH3vLY1EBE3k6EW .image-shape .label,#mermaid-svg-IPH3vLY1EBE3k6EW .icon-shape .label{text-anchor:middle;}#mermaid-svg-IPH3vLY1EBE3k6EW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IPH3vLY1EBE3k6EW .rough-node .label,#mermaid-svg-IPH3vLY1EBE3k6EW .node .label,#mermaid-svg-IPH3vLY1EBE3k6EW .image-shape .label,#mermaid-svg-IPH3vLY1EBE3k6EW .icon-shape .label{text-align:center;}#mermaid-svg-IPH3vLY1EBE3k6EW .node.clickable{cursor:pointer;}#mermaid-svg-IPH3vLY1EBE3k6EW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IPH3vLY1EBE3k6EW .arrowheadPath{fill:#333333;}#mermaid-svg-IPH3vLY1EBE3k6EW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IPH3vLY1EBE3k6EW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IPH3vLY1EBE3k6EW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IPH3vLY1EBE3k6EW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IPH3vLY1EBE3k6EW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IPH3vLY1EBE3k6EW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IPH3vLY1EBE3k6EW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IPH3vLY1EBE3k6EW .cluster text{fill:#333;}#mermaid-svg-IPH3vLY1EBE3k6EW .cluster span{color:#333;}#mermaid-svg-IPH3vLY1EBE3k6EW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IPH3vLY1EBE3k6EW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IPH3vLY1EBE3k6EW rect.text{fill:none;stroke-width:0;}#mermaid-svg-IPH3vLY1EBE3k6EW .icon-shape,#mermaid-svg-IPH3vLY1EBE3k6EW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IPH3vLY1EBE3k6EW .icon-shape p,#mermaid-svg-IPH3vLY1EBE3k6EW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IPH3vLY1EBE3k6EW .icon-shape rect,#mermaid-svg-IPH3vLY1EBE3k6EW .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IPH3vLY1EBE3k6EW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IPH3vLY1EBE3k6EW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IPH3vLY1EBE3k6EW :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

选举完成后的集群状态

新 LeadermaxZxid=120

Follower1lastZxid=100

Follower2lastZxid=120

Follower3lastZxid=90

Follower4lastZxid=130

节点数据状态需要同步
Leader maxZxid=120 基准节点
Follower1 lastZxid=100 缺失事务 101-120
Follower2 lastZxid=120 已同步,无需操作
Follower3 lastZxid=90 缺失事务 91-120,且落后较多
Follower4 lastZxid=130 有多余事务(可能是旧 Leader)

1.2 数据同步的核心目标

目标说明
一致性 所有节点最终看到相同的数据视图
完整性 已提交的事务不能丢失
正确性 未提交的事务不能出现
高效性 根据落后程度选择最优同步策略

二、同步前的准备工作

2.1 Follower 连接 Leader

选举完成后,Follower 会主动连接新 Leader,并发送自己的状态信息:

新 Leader

Follower

新 Leader

Follower

#mermaid-svg-Q3K1e3rehZSaLnNd{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Q3K1e3rehZSaLnNd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Q3K1e3rehZSaLnNd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Q3K1e3rehZSaLnNd .error-icon{fill:#552222;}#mermaid-svg-Q3K1e3rehZSaLnNd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q3K1e3rehZSaLnNd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Q3K1e3rehZSaLnNd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q3K1e3rehZSaLnNd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q3K1e3rehZSaLnNd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Q3K1e3rehZSaLnNd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q3K1e3rehZSaLnNd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q3K1e3rehZSaLnNd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q3K1e3rehZSaLnNd .marker.cross{stroke:#333333;}#mermaid-svg-Q3K1e3rehZSaLnNd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q3K1e3rehZSaLnNd p{margin:0;}#mermaid-svg-Q3K1e3rehZSaLnNd .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Q3K1e3rehZSaLnNd text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Q3K1e3rehZSaLnNd .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Q3K1e3rehZSaLnNd .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Q3K1e3rehZSaLnNd .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Q3K1e3rehZSaLnNd .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Q3K1e3rehZSaLnNd #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Q3K1e3rehZSaLnNd .sequenceNumber{fill:white;}#mermaid-svg-Q3K1e3rehZSaLnNd #sequencenumber{fill:#333;}#mermaid-svg-Q3K1e3rehZSaLnNd #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Q3K1e3rehZSaLnNd .messageText{fill:#333;stroke:none;}#mermaid-svg-Q3K1e3rehZSaLnNd .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Q3K1e3rehZSaLnNd .labelText,#mermaid-svg-Q3K1e3rehZSaLnNd .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Q3K1e3rehZSaLnNd .loopText,#mermaid-svg-Q3K1e3rehZSaLnNd .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Q3K1e3rehZSaLnNd .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Q3K1e3rehZSaLnNd .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Q3K1e3rehZSaLnNd .noteText,#mermaid-svg-Q3K1e3rehZSaLnNd .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Q3K1e3rehZSaLnNd .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Q3K1e3rehZSaLnNd .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Q3K1e3rehZSaLnNd .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Q3K1e3rehZSaLnNd .actorPopupMenu{position:absolute;}#mermaid-svg-Q3K1e3rehZSaLnNd .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Q3K1e3rehZSaLnNd .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Q3K1e3rehZSaLnNd .actor-man circle,#mermaid-svg-Q3K1e3rehZSaLnNd line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Q3K1e3rehZSaLnNd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

6. 收集所有 Follower 的 lastZxid

7. 确定 minZxid 和 maxZxid

8. 为每个 Follower 决定同步策略

1. 建立 TCP 连接

2. 发送 FOLLOWERINFO(lastZxid)

3. 记录 Follower 的 lastZxid

4. 返回 LEADERINFO(newEpoch)

5. 发送 ACKEPOCH(lastZxid)

2.2 关键数据结构

Leader 端维护的同步信息:

// Leader.java – 维护 Follower 状态
public class Leader {

// 记录每个 Follower 的 lastZxid
private Map<Long, Long> followerLastZxid = new ConcurrentHashMap<>();

// 已提交事务日志缓存
private LinkedList<Proposal> committedLog = new LinkedList<>();

// 最小 ZXID(committedLog 中最旧的事务)
private long minCommittedLog;

// 最大 ZXID(committedLog 中最新的一个)
private long maxCommittedLog;

public void processAckEpoch(QuorumPacket qp, LearnerHandler handler) {
long lastZxid = qp.getZxid();
long sid = handler.getSid();

// 记录 Follower 的 lastZxid
followerLastZxid.put(sid, lastZxid);

// 更新全局最小和最大 ZXID
updateZxidRange(lastZxid);

// 如果收到过半 ACK,开始决定同步策略
if (followerLastZxid.size() > half) {
decideSyncStrategyForAll();
}
}
}

三、四种同步策略详解

Leader 根据 Follower 上报的 lastZxid 与自身 committedLog 的范围比较,决定采用哪种同步方式:

#mermaid-svg-UL7pjXfEQz0jeGsP{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UL7pjXfEQz0jeGsP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UL7pjXfEQz0jeGsP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UL7pjXfEQz0jeGsP .error-icon{fill:#552222;}#mermaid-svg-UL7pjXfEQz0jeGsP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UL7pjXfEQz0jeGsP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UL7pjXfEQz0jeGsP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UL7pjXfEQz0jeGsP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UL7pjXfEQz0jeGsP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UL7pjXfEQz0jeGsP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UL7pjXfEQz0jeGsP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UL7pjXfEQz0jeGsP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UL7pjXfEQz0jeGsP .marker.cross{stroke:#333333;}#mermaid-svg-UL7pjXfEQz0jeGsP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UL7pjXfEQz0jeGsP p{margin:0;}#mermaid-svg-UL7pjXfEQz0jeGsP .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-UL7pjXfEQz0jeGsP .cluster-label text{fill:#333;}#mermaid-svg-UL7pjXfEQz0jeGsP .cluster-label span{color:#333;}#mermaid-svg-UL7pjXfEQz0jeGsP .cluster-label span p{background-color:transparent;}#mermaid-svg-UL7pjXfEQz0jeGsP .label text,#mermaid-svg-UL7pjXfEQz0jeGsP span{fill:#333;color:#333;}#mermaid-svg-UL7pjXfEQz0jeGsP .node rect,#mermaid-svg-UL7pjXfEQz0jeGsP .node circle,#mermaid-svg-UL7pjXfEQz0jeGsP .node ellipse,#mermaid-svg-UL7pjXfEQz0jeGsP .node polygon,#mermaid-svg-UL7pjXfEQz0jeGsP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UL7pjXfEQz0jeGsP .rough-node .label text,#mermaid-svg-UL7pjXfEQz0jeGsP .node .label text,#mermaid-svg-UL7pjXfEQz0jeGsP .image-shape .label,#mermaid-svg-UL7pjXfEQz0jeGsP .icon-shape .label{text-anchor:middle;}#mermaid-svg-UL7pjXfEQz0jeGsP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UL7pjXfEQz0jeGsP .rough-node .label,#mermaid-svg-UL7pjXfEQz0jeGsP .node .label,#mermaid-svg-UL7pjXfEQz0jeGsP .image-shape .label,#mermaid-svg-UL7pjXfEQz0jeGsP .icon-shape .label{text-align:center;}#mermaid-svg-UL7pjXfEQz0jeGsP .node.clickable{cursor:pointer;}#mermaid-svg-UL7pjXfEQz0jeGsP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UL7pjXfEQz0jeGsP .arrowheadPath{fill:#333333;}#mermaid-svg-UL7pjXfEQz0jeGsP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UL7pjXfEQz0jeGsP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UL7pjXfEQz0jeGsP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UL7pjXfEQz0jeGsP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UL7pjXfEQz0jeGsP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UL7pjXfEQz0jeGsP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UL7pjXfEQz0jeGsP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UL7pjXfEQz0jeGsP .cluster text{fill:#333;}#mermaid-svg-UL7pjXfEQz0jeGsP .cluster span{color:#333;}#mermaid-svg-UL7pjXfEQz0jeGsP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UL7pjXfEQz0jeGsP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UL7pjXfEQz0jeGsP rect.text{fill:none;stroke-width:0;}#mermaid-svg-UL7pjXfEQz0jeGsP .icon-shape,#mermaid-svg-UL7pjXfEQz0jeGsP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UL7pjXfEQz0jeGsP .icon-shape p,#mermaid-svg-UL7pjXfEQz0jeGsP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UL7pjXfEQz0jeGsP .icon-shape rect,#mermaid-svg-UL7pjXfEQz0jeGsP .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UL7pjXfEQz0jeGsP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UL7pjXfEQz0jeGsP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UL7pjXfEQz0jeGsP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

等于 Leader.maxZxid

小于 Leader.minZxid

在 Leader.minZxid 和 maxZxid 之间

大于 Leader.maxZxid

收到 Follower 的 lastZxid

比较 lastZxid

无需同步直接进入服务

SNAP 全量同步发送完整快照

DIFF 增量同步发送缺失事务

TRUNC 截断同步回滚多余事务

同步完成

3.1 DIFF 同步(差异化同步)

触发条件:Leader.minZxid ≤ Follower.lastZxid ≤ Leader.maxZxid

适用场景:Follower 只落后少量事务,是最常见的同步方式。

Leader (min=90, max=120)

Follower (lastZxid=100)

Leader (min=90, max=120)

Follower (lastZxid=100)

#mermaid-svg-4gOe0W1PZqhwLYbe{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4gOe0W1PZqhwLYbe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4gOe0W1PZqhwLYbe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4gOe0W1PZqhwLYbe .error-icon{fill:#552222;}#mermaid-svg-4gOe0W1PZqhwLYbe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4gOe0W1PZqhwLYbe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4gOe0W1PZqhwLYbe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4gOe0W1PZqhwLYbe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4gOe0W1PZqhwLYbe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4gOe0W1PZqhwLYbe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4gOe0W1PZqhwLYbe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4gOe0W1PZqhwLYbe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4gOe0W1PZqhwLYbe .marker.cross{stroke:#333333;}#mermaid-svg-4gOe0W1PZqhwLYbe svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4gOe0W1PZqhwLYbe p{margin:0;}#mermaid-svg-4gOe0W1PZqhwLYbe .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4gOe0W1PZqhwLYbe text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-4gOe0W1PZqhwLYbe .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4gOe0W1PZqhwLYbe .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-4gOe0W1PZqhwLYbe .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-4gOe0W1PZqhwLYbe .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-4gOe0W1PZqhwLYbe #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-4gOe0W1PZqhwLYbe .sequenceNumber{fill:white;}#mermaid-svg-4gOe0W1PZqhwLYbe #sequencenumber{fill:#333;}#mermaid-svg-4gOe0W1PZqhwLYbe #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-4gOe0W1PZqhwLYbe .messageText{fill:#333;stroke:none;}#mermaid-svg-4gOe0W1PZqhwLYbe .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4gOe0W1PZqhwLYbe .labelText,#mermaid-svg-4gOe0W1PZqhwLYbe .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-4gOe0W1PZqhwLYbe .loopText,#mermaid-svg-4gOe0W1PZqhwLYbe .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-4gOe0W1PZqhwLYbe .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4gOe0W1PZqhwLYbe .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-4gOe0W1PZqhwLYbe .noteText,#mermaid-svg-4gOe0W1PZqhwLYbe .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-4gOe0W1PZqhwLYbe .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4gOe0W1PZqhwLYbe .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4gOe0W1PZqhwLYbe .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4gOe0W1PZqhwLYbe .actorPopupMenu{position:absolute;}#mermaid-svg-4gOe0W1PZqhwLYbe .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-4gOe0W1PZqhwLYbe .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4gOe0W1PZqhwLYbe .actor-man circle,#mermaid-svg-4gOe0W1PZqhwLYbe line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-4gOe0W1PZqhwLYbe :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

1. DIFF 指令

2. PROPOSAL(zxid=101)

2. PROPOSAL(zxid=102)

2. PROPOSAL(zxid=103)

…(直到 zxid=120)

3. 写入事务日志

4. 等待 COMMIT

5. COMMIT(zxid=101)

5. COMMIT(zxid=102)

5. COMMIT(zxid=103)

…(按顺序提交)

6. 应用到内存

7. 同步完成

源码实现:

// LearnerHandler.java – DIFF 同步
private void syncFollower(long lastZxid) {
// 获取从 lastZxid+1 开始的所有提案
Iterator<Proposal> it = leader.getCommittedLog()
.iterator(lastZxid + 1);

// 发送 DIFF 指令
queuePacket(new QuorumPacket(Leader.DIFF, 0, null, null));

// 按顺序发送缺失的提案
while (it.hasNext()) {
Proposal p = it.next();
queuePacket(p.packet);
}

// 发送 COMMIT 消息
it = leader.getCommittedLog().iterator(lastZxid + 1);
while (it.hasNext()) {
Proposal p = it.next();
queuePacket(new QuorumPacket(Leader.COMMIT, p.packet.getZxid(), null, null));
}
}

3.2 TRUNC 同步(截断同步)

触发条件:Follower.lastZxid > Leader.maxZxid

适用场景:Follower 是旧 Leader 恢复后重新加入,有多余的事务需要删除。

Leader (max=120)

Follower (lastZxid=130)

Leader (max=120)

Follower (lastZxid=130)

#mermaid-svg-TM8FXd15VskJiJR0{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TM8FXd15VskJiJR0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TM8FXd15VskJiJR0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TM8FXd15VskJiJR0 .error-icon{fill:#552222;}#mermaid-svg-TM8FXd15VskJiJR0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TM8FXd15VskJiJR0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TM8FXd15VskJiJR0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TM8FXd15VskJiJR0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TM8FXd15VskJiJR0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TM8FXd15VskJiJR0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TM8FXd15VskJiJR0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TM8FXd15VskJiJR0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TM8FXd15VskJiJR0 .marker.cross{stroke:#333333;}#mermaid-svg-TM8FXd15VskJiJR0 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TM8FXd15VskJiJR0 p{margin:0;}#mermaid-svg-TM8FXd15VskJiJR0 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-TM8FXd15VskJiJR0 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-TM8FXd15VskJiJR0 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-TM8FXd15VskJiJR0 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-TM8FXd15VskJiJR0 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-TM8FXd15VskJiJR0 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-TM8FXd15VskJiJR0 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-TM8FXd15VskJiJR0 .sequenceNumber{fill:white;}#mermaid-svg-TM8FXd15VskJiJR0 #sequencenumber{fill:#333;}#mermaid-svg-TM8FXd15VskJiJR0 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-TM8FXd15VskJiJR0 .messageText{fill:#333;stroke:none;}#mermaid-svg-TM8FXd15VskJiJR0 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-TM8FXd15VskJiJR0 .labelText,#mermaid-svg-TM8FXd15VskJiJR0 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-TM8FXd15VskJiJR0 .loopText,#mermaid-svg-TM8FXd15VskJiJR0 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-TM8FXd15VskJiJR0 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-TM8FXd15VskJiJR0 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-TM8FXd15VskJiJR0 .noteText,#mermaid-svg-TM8FXd15VskJiJR0 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-TM8FXd15VskJiJR0 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-TM8FXd15VskJiJR0 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-TM8FXd15VskJiJR0 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-TM8FXd15VskJiJR0 .actorPopupMenu{position:absolute;}#mermaid-svg-TM8FXd15VskJiJR0 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-TM8FXd15VskJiJR0 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-TM8FXd15VskJiJR0 .actor-man circle,#mermaid-svg-TM8FXd15VskJiJR0 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-TM8FXd15VskJiJR0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

告诉 Follower 回滚到 zxid=120

1. TRUNC 指令

2. 删除 zxid 121-130 的事务日志

3. 回滚内存数据

4. 回滚完成确认

源码实现:

// LearnerHandler.java – TRUNC 同步
private void handleTruncSync(long lastZxid) {
long leaderZxid = leader.getMaxCommittedLog();

// 发送 TRUNC 指令
QuorumPacket trunc = new QuorumPacket(Leader.TRUNC, leaderZxid, null, null);
queuePacket(trunc);

LOG.info("Sending TRUNC to follower, follower lastZxid={}, leader lastZxid={}",
Long.toHexString(lastZxid), Long.toHexString(leaderZxid));
}

3.3 SNAP 同步(全量快照同步)

触发条件:Follower.lastZxid < Leader.minZxid

适用场景:Follower 落后太多(超过 committedLog 缓存范围),或新节点首次加入。

Leader (min=90, max=120)

Follower (lastZxid=50)

Leader (min=90, max=120)

Follower (lastZxid=50)

#mermaid-svg-DFZfgdIDhoV4z3eD{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DFZfgdIDhoV4z3eD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DFZfgdIDhoV4z3eD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DFZfgdIDhoV4z3eD .error-icon{fill:#552222;}#mermaid-svg-DFZfgdIDhoV4z3eD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DFZfgdIDhoV4z3eD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DFZfgdIDhoV4z3eD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DFZfgdIDhoV4z3eD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DFZfgdIDhoV4z3eD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DFZfgdIDhoV4z3eD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DFZfgdIDhoV4z3eD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DFZfgdIDhoV4z3eD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DFZfgdIDhoV4z3eD .marker.cross{stroke:#333333;}#mermaid-svg-DFZfgdIDhoV4z3eD svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DFZfgdIDhoV4z3eD p{margin:0;}#mermaid-svg-DFZfgdIDhoV4z3eD .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DFZfgdIDhoV4z3eD text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-DFZfgdIDhoV4z3eD .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-DFZfgdIDhoV4z3eD .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-DFZfgdIDhoV4z3eD .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-DFZfgdIDhoV4z3eD .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-DFZfgdIDhoV4z3eD #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-DFZfgdIDhoV4z3eD .sequenceNumber{fill:white;}#mermaid-svg-DFZfgdIDhoV4z3eD #sequencenumber{fill:#333;}#mermaid-svg-DFZfgdIDhoV4z3eD #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-DFZfgdIDhoV4z3eD .messageText{fill:#333;stroke:none;}#mermaid-svg-DFZfgdIDhoV4z3eD .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DFZfgdIDhoV4z3eD .labelText,#mermaid-svg-DFZfgdIDhoV4z3eD .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-DFZfgdIDhoV4z3eD .loopText,#mermaid-svg-DFZfgdIDhoV4z3eD .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-DFZfgdIDhoV4z3eD .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-DFZfgdIDhoV4z3eD .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-DFZfgdIDhoV4z3eD .noteText,#mermaid-svg-DFZfgdIDhoV4z3eD .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-DFZfgdIDhoV4z3eD .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DFZfgdIDhoV4z3eD .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DFZfgdIDhoV4z3eD .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DFZfgdIDhoV4z3eD .actorPopupMenu{position:absolute;}#mermaid-svg-DFZfgdIDhoV4z3eD .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-DFZfgdIDhoV4z3eD .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DFZfgdIDhoV4z3eD .actor-man circle,#mermaid-svg-DFZfgdIDhoV4z3eD line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-DFZfgdIDhoV4z3eD :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

2. 生成内存数据快照

1. SNAP 指令

3. 序列化 DataTree

4. 发送快照数据

5. 清空当前数据

6. 加载快照

7. 快照加载完成

源码实现:

// LearnerHandler.java – SNAP 同步
private void handleSnapSync() {
// 1. 获取内存数据快照
ByteBuffer snapshot = leader.getZKDatabase().getSnapshot();

// 2. 发送 SNAP 指令
queuePacket(new QuorumPacket(Leader.SNAP,
leader.getMaxCommittedLog(),
snapshot.array(),
null));

// 3. 发送 UPTODATE 确认
queuePacket(new QuorumPacket(Leader.UPTODATE, 1, null, null));
}

3.4 TRUNC+DIFF 同步(复合同步)

触发条件:Follower 既有缺失的事务,又有多余的事务

适用场景:复杂的网络分区故障后恢复。

Leader (min=95, max=120)

Follower (lastZxid=125)

Leader (min=95, max=120)

Follower (lastZxid=125)

#mermaid-svg-QUmjjThmEVSRLwLD{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QUmjjThmEVSRLwLD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QUmjjThmEVSRLwLD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QUmjjThmEVSRLwLD .error-icon{fill:#552222;}#mermaid-svg-QUmjjThmEVSRLwLD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QUmjjThmEVSRLwLD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QUmjjThmEVSRLwLD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QUmjjThmEVSRLwLD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QUmjjThmEVSRLwLD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QUmjjThmEVSRLwLD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QUmjjThmEVSRLwLD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QUmjjThmEVSRLwLD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QUmjjThmEVSRLwLD .marker.cross{stroke:#333333;}#mermaid-svg-QUmjjThmEVSRLwLD svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QUmjjThmEVSRLwLD p{margin:0;}#mermaid-svg-QUmjjThmEVSRLwLD .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QUmjjThmEVSRLwLD text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-QUmjjThmEVSRLwLD .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QUmjjThmEVSRLwLD .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-QUmjjThmEVSRLwLD .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-QUmjjThmEVSRLwLD .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-QUmjjThmEVSRLwLD #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-QUmjjThmEVSRLwLD .sequenceNumber{fill:white;}#mermaid-svg-QUmjjThmEVSRLwLD #sequencenumber{fill:#333;}#mermaid-svg-QUmjjThmEVSRLwLD #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-QUmjjThmEVSRLwLD .messageText{fill:#333;stroke:none;}#mermaid-svg-QUmjjThmEVSRLwLD .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QUmjjThmEVSRLwLD .labelText,#mermaid-svg-QUmjjThmEVSRLwLD .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-QUmjjThmEVSRLwLD .loopText,#mermaid-svg-QUmjjThmEVSRLwLD .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-QUmjjThmEVSRLwLD .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-QUmjjThmEVSRLwLD .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-QUmjjThmEVSRLwLD .noteText,#mermaid-svg-QUmjjThmEVSRLwLD .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-QUmjjThmEVSRLwLD .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QUmjjThmEVSRLwLD .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QUmjjThmEVSRLwLD .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-QUmjjThmEVSRLwLD .actorPopupMenu{position:absolute;}#mermaid-svg-QUmjjThmEVSRLwLD .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-QUmjjThmEVSRLwLD .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-QUmjjThmEVSRLwLD .actor-man circle,#mermaid-svg-QUmjjThmEVSRLwLD line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-QUmjjThmEVSRLwLD :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

2. 回滚到 zxid=120

1. TRUNC 指令

截断到 zxid=120

3. DIFF 指令

4. PROPOSAL(zxid=96-120)

5. COMMIT(zxid=96-120)

6. 应用事务

四、同步完成确认

无论采用哪种同步策略,最后都需要完成确认:

Leader

Follower

Leader

Follower

#mermaid-svg-RxOqriXbVMapgV8O{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RxOqriXbVMapgV8O .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RxOqriXbVMapgV8O .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RxOqriXbVMapgV8O .error-icon{fill:#552222;}#mermaid-svg-RxOqriXbVMapgV8O .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RxOqriXbVMapgV8O .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RxOqriXbVMapgV8O .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RxOqriXbVMapgV8O .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RxOqriXbVMapgV8O .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RxOqriXbVMapgV8O .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RxOqriXbVMapgV8O .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RxOqriXbVMapgV8O .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RxOqriXbVMapgV8O .marker.cross{stroke:#333333;}#mermaid-svg-RxOqriXbVMapgV8O svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RxOqriXbVMapgV8O p{margin:0;}#mermaid-svg-RxOqriXbVMapgV8O .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RxOqriXbVMapgV8O text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-RxOqriXbVMapgV8O .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-RxOqriXbVMapgV8O .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-RxOqriXbVMapgV8O .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-RxOqriXbVMapgV8O .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-RxOqriXbVMapgV8O #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-RxOqriXbVMapgV8O .sequenceNumber{fill:white;}#mermaid-svg-RxOqriXbVMapgV8O #sequencenumber{fill:#333;}#mermaid-svg-RxOqriXbVMapgV8O #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-RxOqriXbVMapgV8O .messageText{fill:#333;stroke:none;}#mermaid-svg-RxOqriXbVMapgV8O .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RxOqriXbVMapgV8O .labelText,#mermaid-svg-RxOqriXbVMapgV8O .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-RxOqriXbVMapgV8O .loopText,#mermaid-svg-RxOqriXbVMapgV8O .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-RxOqriXbVMapgV8O .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-RxOqriXbVMapgV8O .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-RxOqriXbVMapgV8O .noteText,#mermaid-svg-RxOqriXbVMapgV8O .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-RxOqriXbVMapgV8O .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RxOqriXbVMapgV8O .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RxOqriXbVMapgV8O .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RxOqriXbVMapgV8O .actorPopupMenu{position:absolute;}#mermaid-svg-RxOqriXbVMapgV8O .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-RxOqriXbVMapgV8O .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RxOqriXbVMapgV8O .actor-man circle,#mermaid-svg-RxOqriXbVMapgV8O line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-RxOqriXbVMapgV8O :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

同步完成

收到过半 ACK

状态变更为 FOLLOWING

状态为 LEADING

集群恢复服务

NEWLEADER

如果是 SNAP,生成快照

ACK

UPTODATE

五、完整数据同步示例

5.1 场景一:3节点集群数据同步

Leader (max=120)

Follower2 (last=120)

Follower1 (last=100)

Leader (max=120)

Follower2 (last=120)

Follower1 (last=100)

#mermaid-svg-LsHBd5dIxegqIcPG{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LsHBd5dIxegqIcPG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LsHBd5dIxegqIcPG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LsHBd5dIxegqIcPG .error-icon{fill:#552222;}#mermaid-svg-LsHBd5dIxegqIcPG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LsHBd5dIxegqIcPG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LsHBd5dIxegqIcPG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LsHBd5dIxegqIcPG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LsHBd5dIxegqIcPG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LsHBd5dIxegqIcPG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LsHBd5dIxegqIcPG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LsHBd5dIxegqIcPG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LsHBd5dIxegqIcPG .marker.cross{stroke:#333333;}#mermaid-svg-LsHBd5dIxegqIcPG svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LsHBd5dIxegqIcPG p{margin:0;}#mermaid-svg-LsHBd5dIxegqIcPG .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LsHBd5dIxegqIcPG text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-LsHBd5dIxegqIcPG .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-LsHBd5dIxegqIcPG .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-LsHBd5dIxegqIcPG .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-LsHBd5dIxegqIcPG .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-LsHBd5dIxegqIcPG #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-LsHBd5dIxegqIcPG .sequenceNumber{fill:white;}#mermaid-svg-LsHBd5dIxegqIcPG #sequencenumber{fill:#333;}#mermaid-svg-LsHBd5dIxegqIcPG #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-LsHBd5dIxegqIcPG .messageText{fill:#333;stroke:none;}#mermaid-svg-LsHBd5dIxegqIcPG .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LsHBd5dIxegqIcPG .labelText,#mermaid-svg-LsHBd5dIxegqIcPG .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-LsHBd5dIxegqIcPG .loopText,#mermaid-svg-LsHBd5dIxegqIcPG .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-LsHBd5dIxegqIcPG .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-LsHBd5dIxegqIcPG .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-LsHBd5dIxegqIcPG .noteText,#mermaid-svg-LsHBd5dIxegqIcPG .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-LsHBd5dIxegqIcPG .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LsHBd5dIxegqIcPG .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LsHBd5dIxegqIcPG .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LsHBd5dIxegqIcPG .actorPopupMenu{position:absolute;}#mermaid-svg-LsHBd5dIxegqIcPG .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-LsHBd5dIxegqIcPG .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LsHBd5dIxegqIcPG .actor-man circle,#mermaid-svg-LsHBd5dIxegqIcPG line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-LsHBd5dIxegqIcPG :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Leader 选举完成

par

[并行同步]

收到过半 ACK

同步完成

FOLLOWERINFO(100)

决定 DIFF 同步

DIFF

PROPOSAL(101-120)

COMMIT(101-120)

ACK

FOLLOWERINFO(120)

已同步,无需操作

UPTODATE

ACK

NEWLEADER

NEWLEADER

ACK

ACK

UPTODATE

UPTODATE

5.2 命令行验证

# 1. 启动集群(3节点)
./bin/zkServer.sh start

# 2. 创建一些节点
./bin/zkCli.sh -server localhost:2181 create /sync-test "data"
./bin/zkCli.sh -server localhost:2181 create /sync-test/node1 "node1"
./bin/zkCli.sh -server localhost:2181 create /sync-test/node2 "node2"

# 3. 停止 Leader
./bin/zkServer.sh stop

# 4. 重新启动原 Leader
./bin/zkServer.sh start

# 5. 查看日志中的数据同步过程
tail -f logs/zookeeper.out | grep -E "SYNC|DIFF|TRUNC|SNAP"

# 预期输出
2024-01-01 10:00:01 – Synchronizing with follower 2
2024-01-01 10:00:01 – Using DIFF sync for follower 2
2024-01-01 10:00:02 – Sending proposals from 101 to 120
2024-01-01 10:00:03 – UPTODATE sent to follower 2

六、数据同步的源码分析

6.1 Leader 端决策逻辑

// Leader.java – 决定同步策略
public class Leader {

public void decideSyncStrategy(LearnerHandler handler, long lastZxid) {
long minZxid = getMinCommittedLog();
long maxZxid = getMaxCommittedLog();

if (lastZxid == maxZxid) {
// 已同步,直接发送 UPTODATE
handler.queuePacket(new QuorumPacket(Leader.UPTODATE, 1, null, null));

} else if (lastZxid > maxZxid) {
// Follower 有多余事务,需要截断
LOG.info("Follower has newer data, sending TRUNC to {}", handler.getSid());
handler.queuePacket(new QuorumPacket(Leader.TRUNC, maxZxid, null, null));

} else if (lastZxid < minZxid) {
// Follower 落后太多,需要全量同步
LOG.info("Follower is too old, sending SNAP to {}", handler.getSid());
handleSnapSync(handler);

} else {
// Follower 落后少量事务,增量同步
LOG.info("Follower is slightly behind, sending DIFF to {}", handler.getSid());
handleDiffSync(handler, lastZxid);
}
}
}

6.2 Follower 端处理逻辑

// Learner.java – Follower 处理同步
public class Learner {

public void syncWithLeader(long newLeaderZxid) throws Exception {
// 接收 Leader 的同步指令
QuorumPacket qp = leaderServer.readPacket();

if (qp.getType() == Leader.DIFF) {
// 差异化同步
LOG.info("Received DIFF from leader");
snapshotNeeded = false;

} else if (qp.getType() == Leader.TRUNC) {
// 截断同步
long zxid = qp.getZxid();
zk.getZKDatabase().truncateLog(zxid);
LOG.info("Truncated log to {}", Long.toHexString(zxid));

} else if (qp.getType() == Leader.SNAP) {
// 全量同步
LOG.info("Received SNAP from leader");
snapshotNeeded = true;

// 应用快照
zk.getZKDatabase().clear();
zk.getZKDatabase().deserializeSnapshot(leaderServer.getInputStream());
}

// 同步完成后确认
leaderServer.writePacket(new QuorumPacket(Leader.ACK, 0, null, null));
}
}

七、总结

7.1 四种同步策略对比

策略触发条件操作适用场景性能
DIFF minZxid ≤ lastZxid ≤ maxZxid 增量发送缺失事务 短暂故障后恢复
TRUNC lastZxid > maxZxid 回滚多余事务 旧 Leader 恢复
SNAP lastZxid < minZxid 发送完整快照 新节点或长时间故障
TRUNC+DIFF 复杂情况 先回滚后增量 网络分区恢复 中等

7.2 数据同步完整流程图

#mermaid-svg-r8u1q6BoFsIFxstv{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-r8u1q6BoFsIFxstv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-r8u1q6BoFsIFxstv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-r8u1q6BoFsIFxstv .error-icon{fill:#552222;}#mermaid-svg-r8u1q6BoFsIFxstv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-r8u1q6BoFsIFxstv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-r8u1q6BoFsIFxstv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-r8u1q6BoFsIFxstv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-r8u1q6BoFsIFxstv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-r8u1q6BoFsIFxstv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-r8u1q6BoFsIFxstv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-r8u1q6BoFsIFxstv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-r8u1q6BoFsIFxstv .marker.cross{stroke:#333333;}#mermaid-svg-r8u1q6BoFsIFxstv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-r8u1q6BoFsIFxstv p{margin:0;}#mermaid-svg-r8u1q6BoFsIFxstv .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-r8u1q6BoFsIFxstv .cluster-label text{fill:#333;}#mermaid-svg-r8u1q6BoFsIFxstv .cluster-label span{color:#333;}#mermaid-svg-r8u1q6BoFsIFxstv .cluster-label span p{background-color:transparent;}#mermaid-svg-r8u1q6BoFsIFxstv .label text,#mermaid-svg-r8u1q6BoFsIFxstv span{fill:#333;color:#333;}#mermaid-svg-r8u1q6BoFsIFxstv .node rect,#mermaid-svg-r8u1q6BoFsIFxstv .node circle,#mermaid-svg-r8u1q6BoFsIFxstv .node ellipse,#mermaid-svg-r8u1q6BoFsIFxstv .node polygon,#mermaid-svg-r8u1q6BoFsIFxstv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-r8u1q6BoFsIFxstv .rough-node .label text,#mermaid-svg-r8u1q6BoFsIFxstv .node .label text,#mermaid-svg-r8u1q6BoFsIFxstv .image-shape .label,#mermaid-svg-r8u1q6BoFsIFxstv .icon-shape .label{text-anchor:middle;}#mermaid-svg-r8u1q6BoFsIFxstv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-r8u1q6BoFsIFxstv .rough-node .label,#mermaid-svg-r8u1q6BoFsIFxstv .node .label,#mermaid-svg-r8u1q6BoFsIFxstv .image-shape .label,#mermaid-svg-r8u1q6BoFsIFxstv .icon-shape .label{text-align:center;}#mermaid-svg-r8u1q6BoFsIFxstv .node.clickable{cursor:pointer;}#mermaid-svg-r8u1q6BoFsIFxstv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-r8u1q6BoFsIFxstv .arrowheadPath{fill:#333333;}#mermaid-svg-r8u1q6BoFsIFxstv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-r8u1q6BoFsIFxstv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-r8u1q6BoFsIFxstv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r8u1q6BoFsIFxstv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-r8u1q6BoFsIFxstv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r8u1q6BoFsIFxstv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-r8u1q6BoFsIFxstv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-r8u1q6BoFsIFxstv .cluster text{fill:#333;}#mermaid-svg-r8u1q6BoFsIFxstv .cluster span{color:#333;}#mermaid-svg-r8u1q6BoFsIFxstv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-r8u1q6BoFsIFxstv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-r8u1q6BoFsIFxstv rect.text{fill:none;stroke-width:0;}#mermaid-svg-r8u1q6BoFsIFxstv .icon-shape,#mermaid-svg-r8u1q6BoFsIFxstv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r8u1q6BoFsIFxstv .icon-shape p,#mermaid-svg-r8u1q6BoFsIFxstv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-r8u1q6BoFsIFxstv .icon-shape rect,#mermaid-svg-r8u1q6BoFsIFxstv .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r8u1q6BoFsIFxstv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-r8u1q6BoFsIFxstv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-r8u1q6BoFsIFxstv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Leader 选举完成

等待 Follower 连接

收到 FOLLOWERINFO

记录 lastZxid

收到过半 ACK?

为每个 Follower 决定策略

DIFF 同步

TRUNC 同步

SNAP 同步

无需同步

发送 NEWLEADER

等待 Follower ACK

收到过半 ACK?

发送 UPTODATE

集群就绪

7.3 一句话总结

ZooKeeper 在 Leader 选举后,通过 DIFF、TRUNC、SNAP 三种核心同步策略,根据每个 Follower 的 lastZxid 与 Leader 的日志范围进行比对,以最小开销实现所有节点的数据对齐,确保分布式数据的一致性和完整性。

在这里插入图片描述

🌺The End🌺点点关注,收藏不迷路🌺

赞(0)
未经允许不得转载:171主机测评 » ZooKeeper 数据同步机制深度解析:Leader 选举后的数据对齐
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址