24.3 3PC

一句话

3PC 在 2PC 前加了一个 canCommit 阶段,想让参与者在 “协调者失联” 时能安全地自行决定;但只要网络会分区,“超时” 就无法区分 “对方挂了” 与 “对方还在但网不通”——所以 3PC 仍不保证一致,实践中几乎不用。

| 把 3PC 当“2PC 升级版”直接替换 | 多一个 RTT,且分区下仍不安全 | | 不给 canCommit 设超时 | 协调者等不到回复,长时间挂起 |

三个阶段

阶段 协调者 参与者 与 2PC 的差别
① canCommit 问 “你们能不能做” 只判断可行性,不持锁 新增,用来提前排除不可能
② preCommit 发 preCommit 执行到可提交、持锁、写日志 相当于 2PC 的 prepare
③ doCommit 发 doCommit 提交 同 2PC 的 commit

它想解决什么

2PC 的痛点是 “协调者挂 → 参与者持锁等死”。3PC 的思路是:参与者在 preCommit 后若超时,可以自行提交(因为它知道 “能进 preCommit 说明大家都同意过”)。

场景 2PC 3PC
协调者在 prepare 后挂 参与者阻塞 超时后自行提交
协调者在 canCommit 后挂 — 参与者超时后自行回滚
网络分区 阻塞(安全) 可能一部分提交、一部分回滚

为什么仍不安全

经典反例:网络分区把 “已 preCommit 的参与者” 与 “只到 canCommit 的参与者” 分开,前者超时后提交,后者超时后回滚,结果不一致。根因是:3PC 假设 “超时 = 对方故障”,而分区下这个假设不成立。

假设 现实
超时可以判定故障 分区、GC 停顿、网络抖动都像 “故障”
参与者能自行决定 缺少全局信息,只能靠猜测

工程结论

方案 是否阻塞 是否一致(分区下) 实践
2PC 会 一致(牺牲可用性) 数据库 XA、少数强一致场景
3PC 减少阻塞 不一致 少见
共识复制协调者(Raft/Paxos) 多数派可用即不阻塞 一致 现代主流(如 Spanner、TiDB、etcd)

结论:用共识算法复制 “协调者状态”,既解决单点,也避免 3PC 的一致性漏洞。

代码:三段状态机(示意,生产不建议直接用)

协调者状态:  WAIT → PRECOMMIT → COMMIT / ABORT
参与者状态:  INIT → CANCOMMIT → PRECOMMIT → COMMITTED / ABORTED

参与者超时规则的差别(也是 3PC 的全部赌注):
  CANCOMMIT 阶段超时 → 安全地 ABORT      (还没人承诺过)
  PRECOMMIT 阶段超时 → 默认 COMMIT       (大家都同意过 → 赌“协调者会提交”)
问题就在最后一行:分区时“大家都同意过”可能只是本分区的共识,
对面的分区可能根本没收到 preCommit。于是两边分别 commit / abort。

反模式

反模式 后果
以为 3PC 比 2PC “更正确” 分区下不一致,比阻塞更糟
用超时做 “故障判定” 分区/停顿被误判,误提交
在跨地域用 3PC 三阶段 RTT 叠加,延迟高且仍不安全

三条铁律

🔑 超时不能区分 “故障” 与 “分区”;任何基于超时的 “自行决定” 都有风险。

🔑 要 “不阻塞且一致”,只能靠多数派共识。

🔑 3PC 是历史方案;工程上直接用 2PC(短事务)或 Saga(长事务)。

自检

Q1 3PC 的 canCommit 阶段为什么能 “减少阻塞”?

▸ 答案:因为它把 “可行性判断” 提前:如果有人在 canCommit 阶段就回 no,协调者直接 abort,不必进入持锁的 preCommit。它减少的是无效的持锁,而不是分区下的不一致问题。

Q2 3PC 在什么情况下会导致 “部分提交、部分回滚”?

▸ 答案:网络分区导致两组参与者对 “协调者是否还活着” 判断不同:已 preCommit 的一组超时后自行提交,只到 canCommit 的一组超时后自行回滚。没有多数派作为仲裁,两边都 “合理”,结果不一致。

Q3 为什么现代系统用共识算法而不是 3PC?

▸ 答案:因为共识(Raft/Paxos)能给出唯一权威的决策记录:只要多数派可用,协调者状态就能恢复,参与者不必靠超时猜。代价是需要多数派可用(少数派分区期间不可用),但这正是 “一致性优先” 的正确取舍。

Q4 2PC 与 3PC 的消息量差多少?

▸ 答案:2PC 两轮(prepare + commit),3PC 三轮(canCommit + preCommit + doCommit):多一个 RTT 与一轮日志。跨机房时 3PC 的额外延迟往往超过它 “减少阻塞” 带来的收益。

Q5 如果必须用 2PC,怎么降低阻塞风险?

▸ 答案:① 协调者高可用(共识复制状态);② 事务尽量短(减少持锁时间);③ 参与者侧设超时与人工介入流程;④ 用 presumed abort 减少不确定窗口;⑤ 监控 “长时间 prepare” 的事务并告警。

Q7 如果给 3PC 加一条 “必须问多数派” 的规则,它还是 3PC 吗?

▸ 答案:不是了——那已经变成 共识算法(多数派投票 + 状态机复制)。这也是工程界的结论:与其给 3PC 打补丁,不如直接用 Raft/Paxos 复制协调者状态,既解决单点又不丢一致性。

关联