Basic Paxos 的原理是什么?两个阶段做什么?
简化版
Basic Paxos 是用来让多个节点在可能宕机、网络延迟、消息乱序的环境下,对某一个值达成一致的共识算法。它把达成共识拆成两个阶段:Prepare 阶段先抢提案编号并探测历史,Accept 阶段再提交具体值。核心规则是提案编号单调递增、超过半数节点承诺和接受,利用多数派交集保证不会出现两个不同的已选定值。
详细版
Basic Paxos 里有三个角色:Proposer 提议值,Acceptor 投票接受值,Learner 学习最终结果。真实系统中一个节点常常同时承担多个角色。
Prepare 阶段:Proposer 选择一个全局递增的提案编号 n,向多数 Acceptor 发送 Prepare(n)。Acceptor 如果没有承诺过更大的编号,就承诺以后不再接受小于 n 的提案,并返回自己曾经接受过的最大编号提案和值。
Accept 阶段:Proposer 收到多数 Prepare 响应后,不能随便使用自己的值。如果响应里有人返回了已接受值,必须选择编号最大的那个已接受值;如果没人接受过值,才可以使用自己的新值。随后 Proposer 发送 Accept(n, value)。多数 Acceptor 接受后,这个值就被选定。
Basic Paxos 的难点不在流程,而在安全性:多数派之间一定有交集,所以后续编号更大的提案一定能从某个交集节点知道之前可能已经被选定的值,从而继续沿用它,避免选出两个不同值。
完整版教学
一、Basic Paxos 要解决的问题
Basic Paxos 解决的是“单个值共识”:在一组节点中,大家最终只能选定一个值,即使部分节点宕机、消息延迟、消息重试,也不能出现一部分节点认为 A 被选定、另一部分节点认为 B 被选定的情况。它关注的是安全性优先:只要算法说某个值被选定,就不能再选出另一个不同的值。至于什么时候一定选出来,这是活性问题,Basic Paxos 在冲突严重时可能反复竞争,需要工程上的 Leader 化来改善。
理解 Paxos 时不要先陷在数学证明里,可以抓住一个直觉:系统不相信单个节点,因为单点可能挂、可能延迟、可能说了半截话就没了;系统相信多数派,因为任意两个多数派一定有交集。只要把关键历史信息放在多数派里,后来的提案就一定能从交集节点摸到之前的历史。
二、三个角色分别负责什么
Proposer 负责发起提案,它决定“我想让大家接受哪个值”。Acceptor 负责投票,它保存两个核心状态:已经承诺过的最大提案编号,以及已经接受过的最大提案和值。Learner 负责学习最终结果,通常从多数 Acceptor 的接受结果中得知值已经被选定。
面试里要强调,角色不是机器类型,而是协议职责。一个进程可以同时是 Proposer、Acceptor 和 Learner。比如一个存储副本既可以参与投票,也可以在成为主节点后发起提案。把角色理解成职责,会比理解成固定节点更接近工程实现。
三、Prepare 阶段为什么不能省
Prepare 阶段做两件事:抢编号和探测历史。Proposer 发送 Prepare(n),Acceptor 如果发现 n 比自己承诺过的编号大,就回复承诺:以后不再接受比 n 小的提案。同时,它还要告诉 Proposer 自己曾经接受过的最大编号提案和值。
这个历史返回非常关键。假设某个旧提案已经被多数节点接受,但 Proposer 在通知所有人之前挂了。新 Proposer 并不知道旧值是否已经被选定,如果它直接提交新值,就可能破坏一致性。Prepare 阶段通过多数派交集让新 Proposer 至少能遇到一个保存旧历史的节点,然后把旧值接着往下推进。
四、Accept 阶段为什么要沿用历史值
Proposer 收到多数 Prepare 响应后,如果没有任何 Acceptor 返回已接受值,它可以使用自己的值;如果有人返回已接受值,就必须选择其中提案编号最大的那个值,然后发送 Accept(n, value)。这条规则看起来别扭,但它是 Paxos 安全性的核心。
原因是:编号越大的已接受值越接近“可能已经被选定”的状态。新提案沿用它,相当于把可能已经发生的历史继续推进,而不是另起炉灶。这样即使老提案已经在某个多数派里被接受,新提案也不会选出不同值。Paxos 的精髓就是后来的提案编号可以更大,但值不能随便变。
五、为什么多数派能保证安全
多数派的关键性质是交集。5 个节点里任意 3 个节点组成的多数派,和另一个 3 节点多数派至少共享 1 个节点。这个共享节点会保存已经承诺或接受过的历史。当一个值被多数 Acceptor 接受后,任何后续想获得多数承诺的 Proposer,都绕不开之前多数派里的某个节点,因此能知道历史。
这也是 Paxos 可以容忍少数节点故障的原因:只要还能凑出多数,系统就能继续推进;只要所有决策都经过多数,历史就不会被完全绕过。多数派不是为了“人多力量大”,而是为了“历史不会断”。
六、面试追问与工程边界
常见追问是 Basic Paxos 为什么实际使用少。原因是它只解决单个值,每次写入都要 Prepare 和 Accept 两轮,性能和实现复杂度都不友好。如果有多个 Proposer 同时竞争,还可能互相抢更大的编号,导致长时间选不出来。因此真实系统更常用 Multi-Paxos、Raft、ZAB 这类带稳定 Leader 的变体,把多次写入变成一个连续日志复制问题。
另一个追问是 Paxos 是否保证强一致。准确说,Paxos 是共识协议,用来在副本间就值达成一致;上层系统如果把每次状态变更都通过共识日志提交,再按顺序应用,就可以构建线性一致或强一致的服务。但协议本身只是基础积木,还需要日志、状态机、读策略、成员变更、持久化等工程机制。
七、常见误区与追问
这道题不能只背概念,要把「Basic Paxos」放回真实分布式系统里解释:参与方是谁、状态怎么流转、失败后怎么恢复,以及它在一致性、性能、可用性之间做了什么取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | Basic Paxos 通过 Prepare/Promise 和 Accept/Accepted 两阶段,在多数派接受后确定一个值 | 不要停在名词解释 |
| 流程机制 | Proposer 发送 Prepare(n) -> Acceptor 承诺不接受更小编号 -> Proposer 收集多数派 Promise -> 发送 Accept(n,value) -> 多数派 Accepted 后值被选定 | 说明触发方、存储方、确认点和兜底 |
| 工程取舍 | 5 个节点中多数派是 3,只要某个提案被 3 个节点接受,后续提案必须尊重这个已接受值 | 一致性协议用延迟和可用性换确定顺序,不能只背 Paxos/Raft 名词 |
Basic Paxos 面试拆解:
1. Proposer 发送 Prepare(n)
2. Acceptor 承诺不接受更小编号
3. Proposer 收集多数派 Promise
4. 发送 Accept(n,value)
5. 多数派 Accepted 后值被选定
记忆钩子:先说明故障模型和多数派,再拆选主、日志复制、提交、恢复和安全性边界;回答时要紧扣「Basic Paxos」这道题,不要把相邻概念混成一段泛泛的分布式套话。
- 误区:Paxos 是为了保证所有节点同时成功。 它保证多数派选定同一个值,少数节点可稍后学习补齐。
- 误区:提案编号只是普通自增 ID。 编号必须全局可比较且递增,用来决定承诺和覆盖规则。
- 误区:Prepare 阶段可以省略。 Basic Paxos 依赖 Prepare 发现历史已接受值,避免不同值被多数派选中。
- 追问:为什么多数派能保证安全? 任意两个多数派必有交集,交集节点会携带历史承诺或接受信息。
- 追问:Paxos 难在哪里? 理论安全性强但工程状态多,实现和排障复杂。
- 追问:它和 Raft 怎么比较? Raft 把选主、日志复制和安全性拆得更易理解,Paxos 更抽象。
八、加强记忆
Basic Paxos 可以记成“先问历史,再提值”。Prepare 阶段用更大的编号拿多数承诺,并收集已接受历史;Accept 阶段根据历史选择值,再让多数接受。安全性来自多数派交集:后来的多数派一定会碰到之前多数派中的节点,因此不会忘掉可能已经选定的值。编号可以变大,值必须尊重历史,这是 Paxos 最核心的规则。