引言
在分布式系统中,共识(Consensus)是最核心、也最具挑战性的问题之一。它决定了多个节点如何就某个值达成一致,是构建可靠分布式应用的基石。从 Google Chubby 到 etcd,从 ZooKeeper 到 Consul,共识算法的身影无处不在。然而,面对 Paxos、Raft、ZAB 这三大经典算法,许多开发者往往陷入选择困难:它们到底有什么区别?各自适合什么场景?本文将深入三者的设计哲学、实现细节与工程实践,帮你建立系统性的认知框架。
一、Paxos:理论上的"圣杯"
Paxos 由 Leslie Lamport 于 1998 年提出,被誉为分布式共识领域的"圣杯"。它的核心思想简洁而深刻,却也因难以理解和实现而"臭名昭著"。
1.1 核心角色与两阶段
Paxos 定义了三个核心角色:Proposer(提议者)、Acceptor(接受者)和 Learner(学习者)。算法分为两个阶段:
Phase 1 — Prepare/Promise: Proposer 选择一个全局唯一的提案编号 n,向多数派 Acceptor 发送 Prepare(n) 请求。Acceptor 如果尚未响应过编号大于等于 n 的 Prepare,则承诺不再接受编号小于 n 的提案,并将已接受的最大编号提案(如果有)返回给 Proposer。
Phase 2 — Accept/Accepted: Proposer 收到多数派的 Promise 响应后,根据响应中是否已存在提案值决定自己可以提议什么值。然后向多数派发送 Accept(n, v) 请求。Acceptor 只要未响应过编号大于 n 的 Prepare,就接受该提案。当 Proposer 收到多数派的 Accepted 响应时,该提案即被 chosen(选定)。
1.2 Multi-Paxos 与 Leader 选举
基本 Paxos 每次提案都需要完整执行两阶段,延迟高且吞吐低。Multi-Paxos 通过选举稳定的 Leader 来优化:Leader 只需在第一次提案时执行 Phase 1,后续提案直接进入 Phase 2,大幅降低延迟。Leader 选举本身也是通过一次 Basic Paxos 来完成的(或使用租约机制避免活锁)。
1.3 工程困境
Paxos 的工程化之路充满挑战:活锁问题(多个 Proposer 不断竞争编号导致无法收敛)、实现复杂度(正确处理边界条件如磁盘持久化、网络分区恢复等)、调试困难(非确定性行为导致 bug 难以复现)。这也是为什么 Google 在 Chubby 中实现了 Paxos 后,官方文档仍写道:"The Paxos algorithm, when presented in plain English, is very simple."
二、Raft:为工程而生的设计
2014 年,Diego Ongaro 和 John Ousterhout 提出 Raft 算法,明确将"可理解性"作为首要设计目标。Raft 通过分解和状态简化,让共识算法变得工程友好。
2.1 三大子问题
Raft 将共识问题分解为三个相对独立的子问题:
Leader 选举: Raft 使用随机超时(Randomized Election Timeout)来避免脑裂。每个 Follower 在 election timeout(通常 150ms-300ms 随机)内未收到 Leader 的心跳时,就发起选举。先获得多数票者成为新 Leader。只允许日志至少与其他节点一样新的 Candidate 当选(Log Up-to-Date Rule)。
日志复制: Leader 接收客户端请求,追加到本地日志,然后通过 AppendEntries RPC 并行复制给所有 Follower。当多数派确认后,该条目被 committed(提交),Leader 应用状态机并返回结果。注意:日志条目只从 Leader 流向 Follower,Candidate 永不主动发起写入。
安全性: Raft 通过 Leader Completeness Property 保证——任何已提交的条目必然存在于后续所有 Leader 的日志中。这一规则贯穿选举限制(Candidate 必须包含所有已提交条目)和提交规则(Leader 只能提交当前任期的条目,通过复制间接提交之前任期的条目)。
2.2 日志压缩与快照
随着运行累积,日志无限增长会导致重启恢复变慢。Raft 引入 Snapshot(快照)机制:当日志达到阈值时,Leader 生成当前状态机状态的快照,丢弃之前的快照和相关日志。新加入或严重落后的节点可以通过 InstallSnapshot RPC 快速赶上,避免全量日志传输。
2.3 成员变更
Raft 使用 Joint Consensus(联合共识)处理集群成员变更:在过渡期间同时需要新旧两个配置的多数派同意,避免在切换窗口内因两个不相交的多数派导致脑裂。简化版(如 etcd 的实现)使用单节点变更(Single Server Change),每次只增减一个节点,数学上等价于多次联合共识但更安全易实现。
2.4 为什么 Raft 如此受欢迎
Raft 的成功可归结为四点:强一致性模型(线性一致性)、清晰的状态机分解、详尽的形式化证明(TLA+),以及大量开源实现(etcd、TiKV、Consul、CockroachDB、NATS JetStream 等)。对工程师而言,Raft 不仅是一个算法,更是一个可直接落地的系统设计蓝图。
三、ZAB:为 ZooKeeper 量身打造的共识
ZAB(ZooKeeper Atomic Broadcast)是为 Apache ZooKeeper 专门设计的崩溃恢复原子广播协议。与 Paxos 和 Raft 不同,ZAB 的设计紧密围绕 ZooKeeper 的数据树模型和 Watcher 机制来优化。
3.1 两大模式
ZAB 运行在两种模式下:
恢复模式(Recovery Phase): 当 Leader 崩溃或集群启动时进入恢复模式。首先通过选举协议选出 Leader(基于zxid——64位自增ID,高32位为epoch,低32位为counter),然后 Leader 与 Follower 进行数据同步(DIFF/TRUNC/SNAP策略),直到所有节点数据一致,才退出恢复模式。
广播模式(Broadcast Phase): 恢复完成后进入广播模式。Leader 接收客户端写请求,封装为事务提案(Proposal),按zxid顺序广播给 Follower。Follower 收到提案后写入本地日志并返回 ACK。当收到多数派 ACK 后,Leader 发送 COMMIT 消息使事务生效。这个过程中保证全序关系(Total Order),所有节点以相同顺序执行事务。
3.2 与 Raft 的关键差异
尽管 ZAB 和 Raft 看起来很相似(都有 Leader、有日志复制),它们在几个关键设计上不同:
Leader 选举速度: ZAB 追求最快速的 Leader 选举(甚至允许短暂数据不一致,靠后续的同步修复),而 Raft 的投票机制更保守(必须日志足够新才能当选,但避免了一致性风险)。
ACK 与 COMMIT 分离: Raft 中多数派 ACK 即 COMMIT(committed),ZAB 中多数派 ACK 后 Leader 还需要单独发送 COMMIT,这个差异对延迟稍有影响但给了 ZAB 更多灵活性。
Followers 是否可读: Raft Follower 返回数据可能过时(Leader 还未提交或自己还没同步),ZAB 的 Follower 可以读,但同样可能返回旧数据(取决于sync_limit)。不过两者都支持线性一致性读(Raft 用 ReadIndex/LeaseRead,ZAB 用 sync() + read)。
四、工程选型:避坑指南
三者并非简单的替代关系,它们各自在最擅长的领域发光。以下是实战选型建议:
4.1 何时选 Paxos
极简嵌入式场景:如果只需要轻量级共识(例如状态机复制少于10个节点、且需求固定),可以考虑自实现精简版 Paxos(如直接使用 EPaxos 变体消除 Leader 瓶颈)。另外,跨地域高延迟网络 下 EPaxos(Egalitarian Paxos)可利用无 Leader 设计提升吞吐。
4.2 何时选 Raft
新项目首选:几乎所有新建分布式系统的共识层都用 Raft。etcd(Kubernetes 的元数据存储)、TiKV(TiDB 的存储引擎)、CockroachDB(分布式 SQL)都是最佳实践。Raft 的实现质量和文档丰富度远超其他选择,你大概率不需要从头实现,直接使用 etcd 或 hashicorp/raft 库即可。
强配置管理与服务发现:需要可靠共享配置和协调服务时,基于 Raft 的 etcd 或 Consul 最为合适。
4.3 何时选 ZAB(ZooKeeper)
需要 Watcher 机制的强一致性元数据存储:ZooKeeper 的 Watcher 回调顺序触发模型是实现分布式锁、选主、配置推送等 pattern 的最佳载体。
已有 ZK 技术栈的团队:迁移成本往往比重新搭建 etcd 集群更高,沿用 ZK 并引入 Observer 扩展读能力是明智之举。
Kafka 生态(2.8+除外):之前 Kafka 重度依赖 ZK 管理元数据,3.0+ 开始用 KRaft 去 ZK。如果你还在维护旧版 Kafka 集群,ZAB/ZK 是你的必选项。
4.4 性能指标对比
| 指标 | Paxos (Multi-Paxos) | Raft | ZAB |
|---|---|---|---|
| 典型选举耗时 | 秒级(依赖租约优化) | 150ms-300ms(随机超时) | 最快可 < 200ms |
| 写延迟(提交) | 2 RTT(稳态) | 1 RTT(多数派 ACK) | 2 RTT(Proposal + Commit) |
| 读扩展 | 受限(需 Leader Lease) | Follower 可读(线性一致性用 LeaseRead) | Follower 可读(通过 sync) |
| 成熟实现 | Chubby, Spanner(闭源) | etcd, TiKV, Consul, CockroachDB | ZooKeeper(Apache) |
五、进阶方向:下一代共识演化
分布式共识并没有停留在 Raft/ZAB 时代。以下是几个值得关注的方向:
EPaxos / Caesar: 无 Leader 或对等 Leader 的设计,避免单点瓶颈,特别适合跨地域部署。
硬件辅助共识(如 DPDK、RDMA): 通过 kernel bypass 和远程直接内存访问将共识延迟降到微秒级(如 Nessie、CowBoy 论文)。
混合共识(如快照共识、Streamlet): 用同步假设换取确定性终局性(Finality),适合 Blockchain 和高确定性场景。
总结
分布式共识算法的选型没有万能答案:Paxos 是理论基石、Raft 是工程标杆、ZAB 是生态载体。 对于绝大多数应用场景,从 Raft + etcd 起步是性价比最高的选择。当有特定需求(Watcher、Kafka 生态、超大规模跨地域)时,再考虑 ZAB 或 EPaxos。理解它们的本质差异,才能在分布式系统的构建中游刃有余。
最后,推荐三个学习资源:Raft 官方论文的"可视化章节"(raft.github.io)、Ongaro 的博士论文《Consensus: Bridging Theory and Practice》、以及 Diego Ongaro 在 Stanford 的分布式系统课程——这些资料可以帮助你从"知道"走向"会用"。

发表评论 取消回复