Paxos 算法的每一个步骤(Prepare, Promise, Accept, Accepted)都不是拍脑袋决定的,而是为了在异步网络(乱序、延迟、丢包)中守住一致性的底线。
我们可以通过“为什么不这样做”来反向推导 Paxos 的设计哲学。
1. 为什么必须是“两阶段”,不能直接投票?
- 如果只有一阶段(直接发 Accept):
- 场景:P1 提议 A,P2 提议 B。由于网络延迟,一半节点先收到 A,另一半先收到 B。
- 结果:系统陷入僵局,没有任何一个值能达成多数派,且无法自动恢复。
- Paxos 的解决之道:增加 Prepare 阶段作为“预订”机制。它像是在正式投票前先进行“清场”和“排期”,确保在同一时刻,只有一个高编号的提议能进入投票环节。
2. 为什么 Prepare 阶段只发编号,不发 Value?
- 如果 Prepare 就带上 Value:
- 这会退化成“一阶段提交”。Proposer 在不知道系统现状(是否已有共识)的情况下强行提议,会导致冲突。
- Paxos 的解决之道:Prepare 阶段的本质是 “读”。它先读取多数派的状态,看看有没有前人留下的遗产。只有读清楚了现状,第二阶段的 “写”(Accept)才是安全的。
3. 为什么 Acceptor 必须做出“不再理会小编号”的承诺?
- 如果没有这个承诺:
- 场景:P2 (N=20) 已经完成了 Prepare,准备发 Accept。此时一个极度延迟的 P1 (N=10) 的 Accept 请求突然到达。
- 结果:如果 Acceptor 接受了 P1,就会破坏 P2 刚刚建立的逻辑顺序。
- Paxos 的解决之道:Promise 机制赋予了编号“时间戳”的属性。它强制系统产生逻辑上的先后顺序,让旧的提议在物理时间晚到时自动失效。
4. 为什么 Proposer 必须“认领”反馈中编号最大的旧值?
- 如果没有这个规则:
- 这是最危险的。如果 Proposer 依仗自己编号大就覆盖掉已经达成的旧共识,分布式系统将失去“持久性”。
- Paxos 的解决之道:认领机制实现了“共识的收敛”。它保证了:权力(大编号)可以更迭,但真理(已达成的共识)必须延续。
5. 为什么只需要“多数派”响应?
- 如果要求“全票通过”:
- 只要一个节点宕机,整个系统就瘫痪了。这违背了“高可用”的初衷。
- Paxos 的解决之道:利用 多数派 (Quorum) 的数学交集特性。只要两个多数派有交集,信息就能像接力棒一样传递下去。
总结:Paxos 的设计美学
Paxos 的设计可以浓缩为一句话:用“预订(Prepare)”来发现冲突,用“承诺(Promise)”来规避冲突,用“认领(Feedback)”来化解冲突。
| 设计点 | 解决的问题 | 核心哲学 |
|---|---|---|
| 两阶段 | 异步网络冲突 | 先读后写,谋定而后动 |
| 唯一编号 | 逻辑时序 | 后来者居上,确立权威 |
| 多数派 | 单点故障 | 牺牲绝对冗余,换取高可用 |
| 认领旧值 | 一致性延续 | 尊重历史,强制收敛 |
Paxos 不是在追求“最快”的达成一致,而是在追求“在最混乱的环境下,绝不出错”的极致安全性。