ZAB 协议详解:ZooKeeper 原子广播、消息广播、崩溃恢复与 Leader 选举
ZAB(ZooKeeper Atomic Broadcast,ZooKeeper 原子广播协议)常被误当成 Paxos 的别名,其实它是为 ZooKeeper 量身设计的崩溃可恢复原子广播协议。它的运行可以概括成两种模式的交替:正常时由 Leader 发起全序提案、过半确认后提交;Leader 失联时集群暂停服务,先选举出数据最新的新主,再做数据同步,之后才恢复广播。
为什么 ZooKeeper 需要自己的协议
ZooKeeper 的职责是替业务保管协调状态:配置、选主结果、分布式锁、命名信息。这类数据体量小、写入频率不高,但对顺序和可靠性极其敏感——两个客户端先后拿到”同一把锁”,后果是灾难性的。
因此 ZK 需要一个协议同时满足三点:
- 全序广播:所有写请求在集群范围内排出唯一顺序,各副本按同一顺序应用;
- 崩溃可恢复:Leader 宕机后,已提交的事务不能丢,未提交的半成品不能污染新纪元;
- 主备架构:单一 Leader 串行化写入,Follower 分担读流量。
Paxos 是通用共识理论,直接套用到”原子广播+主备复制”的具体场景并不顺手,于是 ZK 团队设计了 ZAB 这个专用协议。
ZXID:任期与计数器拼出的全序编号
ZAB 用 64 位的 ZXID 给每个事务编序:高 32 位是 epoch(纪元),标识 Leader 任期,新主上位就加一;低 32 位是任期内的事务计数器,每来一个写请求递增一次,换主时清零。
这个结构让”新旧”可以直接比较:epoch 大的任期更新;epoch 相同则计数器大的数据更全。选举和恢复全靠它做裁决依据。
模式一:消息广播
Leader 健康且过半节点已同步时,集群处于广播模式,流程像一个简化版两阶段提交:
Client 写请求 |
两个细节值得记住:
- 过半即提交,不是全员:这与 Raft 的多数派思路一致,3 台容忍 1 台故障、5 台容忍 2 台,所以 ZK 集群推荐奇数台部署。
- fsync 是生命线:Follower 必须等事务日志真正落盘才回 ACK,刷盘延迟直接决定写延迟,生产上要给事务日志单独配低争抢的磁盘。
模式二:崩溃恢复
系统启动或 Leader 与过半节点失联时,集群进入恢复模式,分两步走。
第一步:选举——数据最新者优先
节点进入 LOOKING 状态,互相交换选票 (epoch, zxid, myid),比较顺序是:epoch 大者优先;epoch 相同比 ZXID,大者数据更新;再相同比 myid 大者。拿到过半选票者当选。
让”数据最全”的节点当主不是巧合,而是安全性的关键:新 Leader 天然包含所有已提交事务,恢复时只需别人向它看齐,杜绝了已提交数据被回滚的可能。
第二步:数据同步——补齐、截断或快照
新主把 epoch 加一开启新纪元,然后根据每个 Follower 上报的最新 ZXID 选择同步策略:
- DIFF(差异同步):Follower 只差一点,直接补发缺失提案;
- TRUNC(截断回滚):Follower 残留了旧 Leader 提过但从未提交的”幽灵提案”,必须截掉,防止脏数据混入新纪元;
- SNAP(全量快照):落后太多,直接传快照重建。
epoch 加过半机制还顺带免疫了脑裂:旧 Leader 哪怕只是 Full GC 假死、苏醒后还想发旧 epoch 的提案,也会因为纪元过低被多数派拒绝。过半节点完成同步后,集群退出恢复模式,重新开广播。
与 Raft 对照着看
两者的骨架几乎同构:唯一 Leader、任期概念(epoch 对 term)、多数派提交、选举时偏向日志更新者。差异主要在目标与气质上:
| 维度 | ZAB | Raft |
|---|---|---|
| 设计目标 | ZK 专用的原子广播 | 通用、易理解的共识算法 |
| 主流程划分 | 广播 / 崩溃恢复两模式 | 选举、日志复制、安全性分述 |
| 任期标识 | epoch 内嵌于 ZXID 高位 | term 独立字段 |
| 生态 | 绑定 ZooKeeper | etcd、Consul、TiKV、Kafka KRaft |
一致性边界:顺序一致,而非线性一致
这是使用 ZK 时最容易踩的坑:ZAB 保证所有写按全序被各副本应用(顺序一致性),但不保证你在任意 Follower 上读到最新值。读请求由本地节点直接应答,若它尚未同步到 Leader 的最新位置,客户端拿到的就是旧数据。
需要”写后立刻读到”的场景(如分布式锁的状态校验),必须先调用 sync() 强制当前节点追平 Leader,再执行读。网络分区时连上少数派的客户端也是同理:写会失败,读却可能返回过期数据。把 ZK 当线性一致存储用,是架构层面的误判。
小结
- ZAB 是为 ZooKeeper 定制的原子广播协议,运行分消息广播与崩溃恢复两种模式。
- ZXID 高 32 位 epoch、低 32 位计数器,是选举和同步的裁决依据。
- 广播阶段过半 ACK 即提交;恢复阶段先选数据最新者为新主,再按 DIFF/TRUNC/SNAP 对齐。
- 与 Raft 同构:epoch 对 term、quorum 提交、新主偏向数据更全者。
- ZK 提供顺序一致性而非线性一致性,强读需显式
sync()。

