Zookeeper-ZAB协议#
一句话答案#
ZAB 是 Zookeeper 的原子广播协议,两种模式:崩溃恢复(选主+数据同步)和消息广播(过半确认提交)。
核心要点
ZAB(ZooKeeper Atomic Broadcast)协议: ZooKeeper 使用的原子广播协议,保证分布式数据的一致性。
ZAB 的两种模式:
① 恢复模式(Recovery Mode)
触发条件:Leader 崩溃、集群启动、网络分区恢复
目标:选出新 Leader + 数据同步
流程:
1. 选举阶段:选出拥有最大 ZXID(事务ID)的节点作为准 Leader
2. 发现阶段:准 Leader 收集各 Follower 的最新事务
3. 同步阶段:准 Leader 将最新数据同步给所有 Follower
4. 同步完成,多数 Follower 确认 → 进入广播模式
② 广播模式(Broadcast Mode)
触发条件:Leader 选举完成 + 数据同步完毕
目标:处理客户端写请求
流程:
1. 客户端写请求 → Leader
2. Leader 生成事务提案(Proposal),分配全局递增 ZXID
3. Leader 将 Proposal 发给所有 Follower
4. Follower 写入本地日志 → 返回 ACK
5. Leader 收到半数以上 ACK → 发送 Commit
6. Follower 收到 Commit → 提交事务plaintextZAB vs Raft 对比:
| 维度 | ZAB | Raft |
|---|---|---|
| 设计目标 | 高可用的顺序广播协议(ZooKeeper 专用) | 通用分布式共识算法 |
| Leader 选举 | 选拥有最大 ZXID 的节点 | 随机超时 + 多数投票(日志最新者优先) |
| 任期标识 | epoch(纪元) | term(任期) |
| 日志标识 | ZXID(epoch + counter 组合的 64 位 ID) | term + logIndex |
| 数据同步 | 恢复模式有专门的同步阶段(DIFF / TRUNC / SNAP) | 通过 AppendEntries 逐步对齐 |
| 模式切分 | 显式分为恢复模式和广播模式 | 无显式模式切分,选举和日志复制无缝衔接 |
| 读请求 | 默认 Follower 可读(可能读到旧数据),可通过 sync 强制读 Leader | 默认读 Leader(强一致) |
相同点:
- 都是 Leader-based 的共识协议(单 Leader 负责写入)
- 都需要多数节点(Quorum)确认才能提交
- 都保证已提交的日志不会丢失
- 都通过心跳机制检测 Leader 存活
关键差异总结: ZAB 是为 ZooKeeper 量身定制的,侧重于主备模式下的原子广播和崩溃恢复;Raft 是通用共识算法,更强调可理解性和工程友好性。从实现角度看,ZAB 的恢复阶段更复杂(需要处理多种同步场景),而 Raft 通过简单的日志匹配机制统一处理。
FastLeaderElection 选举算法#
「选 ZXID 最大」只是结论,真正的选举靠 FastLeaderElection(FLE) 通过反复投票 + PK 收敛。
ZXID 的结构: 64 位 = 高 32 位 epoch(选举纪元)+ 低 32 位 counter(该 epoch 内递增的事务序号)。比较 ZXID 即先比 epoch 再比 counter。
投票 PK 规则(依次比较,大者胜): 每张选票携带 (vote_epoch, vote_zxid, vote_myid),节点收到别人的票后与自己当前选票按下面顺序 PK:
1. 先比 electionEpoch(选举轮次/logicalclock)—— 轮次大的更新,直接采纳
2. epoch 相同 → 比 ZXID(事务 id)—— ZXID 大者数据更新,胜
3. ZXID 也相同 → 比 myid(服务器配置 id)—— myid 大者胜(仅用于打破平局)plaintext收敛过程: 每个节点初始投自己;收到一票就按上述规则 PK,若对方选票更优则更新自己的选票并重新广播,否则坚持原票回广播。如此一轮轮 PK,所有节点的选票会快速收敛到「数据最新」的那个节点。
票箱过半判定: 节点统计自己票箱里,投给「当前领先候选者」的票数,一旦超过半数(> N/2)即认定其当选,自己转为 LOOKING→FOLLOWING/LEADING。过半机制保证最多只有一个 Leader 当选。
为何「ZXID 最大」能保证不丢已提交数据#
这是 ZAB 安全性的核心,本质是**「过半提交」+「过半选举」两个多数派必然相交**:
- 一条提案被 commit 的前提是 Leader 收到了过半 Follower 的 ACK,即该事务已复制到一个多数派集合 A。
- 选举要求候选者拿到过半选票,参与投票的也是一个多数派集合 B。
- 任意两个多数派必有交集(鸽巢原理),所以
A ∩ B ≠ ∅:至少有一个既持有该已提交事务、又参与了选举的节点。 - 由于 FLE 选的是 ZXID 最大者,这个交集节点的 ZXID ≥ 该已提交事务的 ZXID,因此选出的新 Leader 的 ZXID 一定 ≥ 任何已提交事务 → 已 commit 的数据不可能丢失。
反向理解:一个没被复制到多数派的事务(未 commit),可能在新 Leader 上被 TRUNC 截断丢弃,这是允许的——ZAB 只承诺「已提交不丢」。
epoch 递增与防脑裂#
恢复阶段 epoch 自增: 新 Leader 选出后进入发现阶段,会取所有 Follower 见过的最大 epoch 加 1 作为新 epoch(newEpoch = maxEpoch + 1),并要求过半 Follower 接受这个新 epoch,写入 acceptedEpoch。此后该任期产生的所有 ZXID 高位都是这个新 epoch。
为何能防旧 Leader 复活脑裂: 假设老 Leader 因网络分区被误判宕机,集群已选出新 Leader 并把 epoch 推到了更高值。等老 Leader 网络恢复回来,它仍用旧 epoch 发提案:
- Follower 发现提案的 epoch 小于自己已接受的
acceptedEpoch,直接拒绝老 Leader 的提案和心跳。 - 老 Leader 收不到过半 ACK,无法提交任何事务,被迫转回 LOOKING 重新参与选举,降级为 Follower。
epoch 单调递增因此充当「逻辑时钟 / 任期号」,保证旧任期的写入永远无法被多数派接受,从根上杜绝了「两个 Leader 同时提交」的脑裂。
面试回答(2分钟版)
ZAB是ZooKeeper专用的原子广播协议,保证分布式数据一致性,有两种工作模式。崩溃恢复模式在Leader挂掉或集群启动时触发,先选举出拥有最大ZXID的节点作为新Leader,然后新Leader将最新数据通过DIFF、TRUNC或SNAP方式同步给所有Follower,多数Follower确认同步完成后进入正常工作。消息广播模式处理客户端写请求,Leader生成Proposal并分配全局递增的ZXID,广播给所有Follower,Follower写入本地日志后返回ACK,Leader收到过半ACK就发送Commit,类似于一个简化的两阶段提交。和Raft对比,两者都是Leader-based的共识协议,都需要多数节点确认才能提交。核心区别在于ZAB是为ZooKeeper量身定制的,有专门的恢复阶段来处理多种同步场景,而Raft是通用共识算法更强调可理解性和工程友好性。另外ZK默认Follower可读可能读到旧数据属于CP系统,选举期间服务不可用。
追问与易错
追问方向:
- “ZAB 和 Raft 核心区别?”→ ZAB 为 ZooKeeper 量身定制有专门的恢复阶段(DIFF/TRUNC/SNAP),Raft 是通用共识算法更强调可理解性;ZAB 用 epoch+ZXID,Raft 用 term+logIndex
- “Leader 选举过程?”→ 每个节点投票给 ZXID 最大的节点(数据最新),获得过半票数的成为 Leader;同 ZXID 时比较 myid,选举期间集群不可用(CP 特性)
- “ZK 适合做注册中心吗?”→ 可以但不是最优,ZK 是 CP 系统选举期间不可用,注册中心更需要 AP(高可用),Nacos/Eureka 在注册发现场景更合适
易错点:
- ❌ ZK 能保证高可用——ZK 是 CP 选举期间不可用
- ❌ ZAB 和 Paxos 完全一样——增加了崩溃恢复