把Paxos换到Raft时踩过的坑

三台机器各记各的账,到底听谁的?

做一套分布式存储原型时,团队卡在了一件事上:元数据和写入顺序不能各节点自己说了算,但任何一台机器都可能宕机、网络也可能抖。查了一圈资料,共识算法是绕不过去的——Paxos 名气大,Raft 好读,最后我们选了 Raft。这篇文章先把 Paxos 和 Raft 各自是什么、解决什么问题、什么场景不得不用讲清楚,再写换算法和落地时踩过的坑。

共识算法到底在解决什么

先把问题说白:你有几台机器存同一份(或同一份逻辑的)数据,客户端往任意节点写,你希望:

  • 写进去的数据不能丢——机器挂了重启后还能读到
  • 大家看到的状态一致——不能 A 节点说余额 100、B 节点说 200
  • 故障时还能继续服务——少数节点挂了,集群整体还能读写

单机没有这个问题,数据在本地磁盘上,顺序天然唯一。分布式一拆,三个麻烦立刻冒出来:

  • 网络分区:机房之间链路断了,两边各自还能跑,但互不知道对方在干什么
  • 节点崩溃:进程挂了、机器断电,已经写到一半的状态没人知道
  • 消息乱序/丢失:RPC 可能重试、延迟、丢包,“先到的请求"不一定"先被处理”

没有共识算法,典型后果是 脑裂(split-brain):两个分区各自认为自己是主,都接受写入,恢复连通后数据对不上,人工 merge 或者直接丢数据。

共识算法要做的就是:在可能出上述故障的环境里,让集群对 “下一个值是什么”“操作按什么顺序执行” 达成一致,并且保证已经确认的结果不会被推翻。

可以把它理解成 分布式里的"拍板机制"——不是选谁当领导那么简单,而是保证拍板结果全体认可、可恢复、可证明安全。

graph TB subgraph 没有共识 C1[客户端写 x=1] --> N1[节点 A: x=1] C2[客户端写 x=2] --> N2[节点 B: x=2] N1 -.网络分区.-> N2 R1[恢复后: 听谁的?] end subgraph 有共识 W[客户端写操作] --> L[Leader 排序] L --> R[复制到多数派] R --> OK[确认后对外可见] end

上面这张图左边是脑裂的典型形态;右边是多数派共识的抽象流程——细节 Paxos 和 Raft 不同,但目标一样:只有被足够多节点确认的状态才算数

什么场景绕不过去

不是所有分布式系统都需要共识。消息队列做最终一致、缓存允许短暂过期、只读副本加主库同步——很多业务用更简单的方式就够了。

下面这几类,如果还要 自动故障转移 + 强一致写入,基本绕不开 Paxos/Raft 或同等能力的协议:

场景共识在干什么典型产品
配置与协调集群成员、路由、开关变更必须全员一致etcd(Kubernetes)、Consul、ZooKeeper
分布式 KV / SQL多副本写入顺序一致,故障后自动选主TiKV、CockroachDB、etcd
分布式锁 / 选主同一时刻只有一个持有者Chubby、etcd lease
元数据管理分片映射、文件 inode、表 schema 不能各存各的HDFS NameNode(ZKFC)、TiDB PD
金融/交易类强一致不能双花、不能重复扣款部分 NewSQL、自研账务核心

不得不用的理由通常就三条,缺一条可以考虑更轻的架构:

  1. 多节点都能写,且不能接受手工修复——宕机后你要自动 failover,不能半夜叫人上去改配置、对账、选主。
  2. 一致性错了有真实代价——重复下单、超卖、双主写入、配置漂移导致全站误切,不是"多等几秒刷新"能糊过去的。
  3. 你要的是线性一致(或接近)的语义——读到的必须是已提交的最新状态,或者能明确说明读到的版本(lease read、read index 等)。

反过来说,不必硬上的情况也常见:单主 + 异步复制够用了;业务允许最终一致;吞吐主要靠分片而不是多主写;团队没有运维多节点 quorum 的能力——硬上共识,故障时的行为比不用还难排查。

FLP 不可能定理:先知道边界

1985 年 Fischer、Lynch、Paterson 证明了 FLP 不可能定理:在纯异步模型里,只要有一个进程可能崩溃,就不存在能在有限步内保证终止的确定性共识算法。

听起来像"共识做不了",实际工程里的出路是 打破纯异步假设

  • 超时 区分"慢"和"死"(Raft 的 election timeout 就是干这个)
  • 接受 短暂不可用(少数派分区写不进去,换一致性换可用性)
  • 随机化 打破对称(Raft 随机选举超时避免活锁)

所以共识不是魔法,是在明确假设下做的权衡。后面选 Paxos 还是 Raft,本质也是在 可理解性、实现成本、生态 之间权衡,不是谁"更正确"。

Paxos:是什么,难在哪

Paxos 是 Leslie Lamport 在 1990 年代提出的共识算法族(经典论文 The Part-Time Parliament,1989 年内部 circulation,1998 年正式发表)。它回答的问题是:在可能丢消息、节点可能宕机的异步系统里,如何让一组节点对单个值(或一系列值)达成一致,且已决定的值不会被改?

三个角色

Paxos 把一次"提案—表决—学习"拆成三个逻辑角色(实际实现里常合并到同一进程):

  • Proposer(提案者):发起编号更高的提案,推动表决
  • Acceptor(接受者):持久化承诺,只有多数派 accept 才算通过
  • Learner(学习者):得知已被选定的值

两阶段在干什么

sequenceDiagram participant P as Proposer participant A as Acceptor participant L as Learner Note over P,A: Prepare 阶段 P->>A: Prepare(n) A->>P: Promise(n, accepted_value) Note over P,A: Accept 阶段 P->>A: Accept(n, value) A->>P: Accepted(n, value) A->>L: Learn(value)
  • Prepare:Proposer 拿编号 n 去占坑,Acceptor 承诺不再接受更小的编号,并告知历史上已 accept 的最大值
  • Accept:Proposer 根据 Prepare 结果选值(若有历史值必须沿用),请求 Accept;多数派 accept 后值被选定

单轮 Paxos 只共识 一个值。工程上要连续日志(Multi-Paxos),需要在外层再叠 稳定 Leader、日志槽位、成员变更——这些在原始论文里并不完整,各实现(Chubby、早期 ZK 等)差异很大。

为什么难

  • 论文抽象层次高:希腊议会比喻有趣,但和"日志复制、快照、成员变更"之间还有一大截工程空白
  • Multi-Paxos 没有标准答案:Leader 怎么选、空洞日志怎么填、成员变更怎么不破坏安全性,都要自己补
  • 活锁:多个 Proposer 互相抬编号,可能长期推不动(工程上靠稳定 Leader 缓解)

为什么还有人用

Paxos 的安全证明扎实,适合已经有一整套 Multi-Paxos 基础设施的团队。Google Chubby(GFS/Bigtable 的锁与选主)、部分存储系统底层用的都是 Paxos 思想。如果你站在巨人肩膀上、改成员变更规则,Paxos 的灵活性是优势。

对大多数从零实现的团队,直接读论文写生产级 Multi-Paxos,成本偏高——这也是我们后来转向 Raft 的主要原因之一。

Raft:是什么,为什么好落地

Raft 是 Diego Ongaro 和 John Ousterhout 2014 年在 Stanford 提出的共识算法,论文标题就写明了目标:In Search of an Understandable Consensus Algorithm。它解决的问题和 Paxos 相同,但 刻意为了教学和生产实现而设计:状态机清晰、Leader 强约束、日志下标和 term 好推理。

三个子问题

Raft 把共识拆成三块相对独立的逻辑:

graph TB A[Raft 核心问题] --> B[Leader 选举] A --> C[日志复制] A --> D[安全性] B --> B1[心跳机制] B --> B2[随机超时] B --> B3[多数派选举] C --> C1[强 Leader 模式] C --> C2[日志连续性] C --> C3[多数派提交] D --> D1[选举限制] D --> D2[日志匹配属性] D --> D3[Leader 完整性]
  • Leader 选举:Follower 超时未收到心跳就竞选;同一 term 最多一个 Leader;得 多数派 选票者胜
  • 日志复制:客户端写只进 Leader;Leader 追加本地日志,复制到 Follower;多数派持久化 后提交,再应用到状态机
  • 安全性:已提交日志不会在后续选举中丢失(选举限制 + Leader 完整性)

和 Paxos 比,Raft 的 强 Leader 把"谁来说话"定死了,Prepare/Accept 那套编号博弈在应用层不那么显眼,读代码、画时序都更直。

工程上为什么更顺手

  • 论文附带 角色状态、RPC、成员变更(joint consensus) 的完整叙事,照着实现能跑
  • etcd、Consul、TiKV、CockroachDB 等开源实现和运维文档成熟,不用从论文 reinvent
  • 调试友好:termindex、Leader/Follower/Candidate 状态一目了然

代价是灵活性略低于 Paxos 大家族——对某些定制 quorum 或异构副本,Raft 改起来要动安全论证;但对常见 3/5/7 节点对称集群,够用了。

实践里怎么选

倾向 Paxos(或现成 Multi-Paxos)

  • 团队已有 Paxos 系基础设施或深度经验
  • 需要特定变种(自定义 quorums、Geo 复制拓扑)
  • 维护的是 Chubby 类协调层,历史包袱在 Paxos 上

倾向 Raft

  • 从零做集群协调、元数据、复制状态机
  • 希望 可维护、可培训、可 code review
  • 依赖 Kubernetes / TiDB / Consul 等现成生态

可以先不碰共识

  • 单主 + 半同步复制满足 SLA
  • 业务接受最终一致(订单状态延迟几秒可见)
  • 写入可以通过 中心化调度(单 writer + 队列)串行化

我们当时的约束是:3 节点起步、要能自动选主、元数据不能丢,团队没人愿意维护自研 Multi-Paxos——选 Raft 是能力匹配,不是 Raft 在理论上压过 Paxos

踩过的坑

理论选对只是开始。Raft 落地时下面几个坑我们实打实踩过。

坑一:网络分区时谁还能写

分区后两个子集都可能还有节点在跑,但 只有包含多数派的那一侧 能完成选举和提交;少数派一侧没有合法 Leader,写请求应失败而不是悄悄写本地。

graph TB subgraph 网络分区 A[5 节点集群分裂] A --> B[分区1: 3 节点<br/>可选举、可提交] A --> C[分区2: 2 节点<br/>选不出 Leader] B --> B1[继续服务] C --> C2[写入必须拒绝] style B1 fill:#90EE90 style C2 fill:#FFB6C1 end

客户端和运维都要接受:共识保一致性,不保证分区期间两边都可写。如果业务要求"任何分区都能写",要么改 quorum 设计(可能牺牲 C),要么就别用强共识。

坑二:重启后日志对不齐

节点宕机重启,本地日志可能比 Leader 短,或者同一 index 上 term 不一致。Raft 用 AppendEntries 一致性检查:Leader 找最后一个匹配点,拒绝冲突后缀,用 Leader 日志覆盖 Follower 冲突段。

实现时容易忽略的是 日志必须持久化再回复 RPC——内存里认为提交了,磁盘没刷,重启后会把已承诺的状态弄丢,安全证明也站不住。

坑三:读也要想清楚

默认"读 Leader 内存状态"在 Leader 切换窗口可能 stale。生产常见几种读:

  • Read Index:先走一遍只读 quorum 或确认自己仍是 Leader,再读状态机
  • Lease Read:Leader 在租约期内认为不会退位,省一次 RPC(时钟漂移要控)

我们早期把所有读都走完整日志复制,延迟明显;后来按业务容忍度拆成强一致读和本地读,吞吐才下来。

坑四:成员变更别一步到位

直接把 3 节点配置改成 5 节点,可能出现 两个不同多数派同时活跃 的窗口。Raft 论文要求 joint consensus 两阶段:先过渡到旧+新成员的联合 quorum,再删掉旧成员。etcd 的 member add/remove API 已经封装了这套,自研时要自己补。

写在最后

Paxos 和 Raft 都是在回答同一个问题:分布式故障下,怎么自动拍板且不重拍。Paxos 更早、更抽象、更灵活;Raft 更整、更好教、生态更贴现代基础设施。

不是所有系统都该上共识——最终一致、单主、队列串行化在很多业务里更便宜。该用的时候,理由通常是:多主或自动切换 + 不能丢已确认状态 + 不能接受脑裂双写

我们 2018 年那次调研结论是 Raft:团队能 hold 住实现和排障,etcd 系工具链也能复用。共识算法理论漂亮,真正耗时间的是分区行为、持久化边界、读语义和运维——这些才是踩坑的高发区。


Paxos 值得读论文建立直觉;Raft 值得读论文加对着 etcd/raft 源码走一遍 RPC。两个都看过,再决定自研还是直接用现成实现,会比只看对比表踏实得多。

可用性说明:本文发布于 2020 年 5 月,距今已超过五年。文中涉及的软件版本、接口、下载地址、命令参数和操作界面可能已经发生变化,部分方案在当前环境下可能失效。请结合官方最新文档核对后再操作,生产环境使用前务必先行验证。

版权声明: 本文首发于 指尖魔法屋-把Paxos换到Raft时踩过的坑https://blog.thinkmoon.cn/post/1-consensus-algorithms-paxos-raft/) 转载或引用必须申明原指尖魔法屋来源及源地址!