两阶段提交(2PC)与三阶段提交(3PC)
本文基于模型知识整理(生成时未联网核对),关键结论建议对照经典文献复核。
一句话定义
两阶段提交(2PC)用"先投票承诺、再统一决定"把跨多个节点的原子提交做出来:全员同意才提交、任一反对即中止;代价是协调者单点与阻塞窗口——协调者在投票后崩溃,全体参与者只能锁着资源等它回来。
为什么重要
2PC 是唯一能跨异构资源(数据库+消息+缓存)提供强原子性的通用协议(XA 标准),也是所有"柔性事务"方案(kp-024)的对照原点。理解它为什么阻塞、为什么与共识形似神异,才能在强一致与高可用之间做出正确架构选择。
前置知识
kp-019(日志与提交语义)、单机事务 ACID。
核心概念
- Phase 1(prepare/vote):协调者询问所有参与者能否提交;参与者把 undo/redo 日志落盘后回复 yes/no——yes 是不可撤销的承诺。
- Phase 2(commit/abort):全员 yes → 协调者发 commit;任一 no 或超时 → abort。参与者执行决定并清理日志。
- 不确定窗口(in-doubt):参与者投了 yes 之后、收到 Phase 2 决定之前,它不能单方面决定(单方面提交可能与其他参与者 abort 矛盾;单方面 abort 则违背已投出的承诺),只能阻塞等协调者。
- 3PC:把 Phase 2 拆成 pre-commit(先同步"将要提交")与 commit,并引入超时默认动作,试图消除阻塞;但分区下仍可能出现两分支各自"提交/中止"的不一致,且多一轮 RTT,实践罕见。
原理与机制
阻塞的根源:决定权收敛到协调者却没有任何参与者能代替它。参与者已 yes 但没收到第二阶段消息时,处于"不确定"状态:此时它若自行 abort,而协调者其实已发出 commit(部分参与者已提交),原子性被破坏;若自行 commit,协调者可能已 abort。所以唯一安全动作是等待——这正是 2PC 在协调者故障时"全员卡死"的机制本质。
2PC ≠ Paxos(形似神异):两者都是两阶段投票,但 2PC 的 Phase 1 承诺的是"我这边准备好了"(资源锁住),参与者没有否决已承诺结果的能力,协调者死掉就死锁;Paxos 的多数派规则允许新 leader 重建决定,无阻塞。因此高可用的原子提交 = 2PC 的语义 + 共识的容错 = 每一步决定都过共识(Spanner/TiDB 的做法:协调者状态与参与者决策日志都放 Raft 组里,kp-032)。
性能:两次"全参与者 RTT" + 准备阶段锁持有时间 = 事务关键路径被最慢参与者绑架;这也是微服务架构普遍回避 XA、转向最终一致的原因之一。
图示
协调者 参与者A 参与者B
│──prepare──────► 锁资源,写日志 锁资源,写日志
│◄──────yes──────────│──────────────│
│◄──────yes──────────(B 也 yes)
│──commit───────► 提交 提交
✗ 若协调者在两阶段间崩溃:
A、B 持锁进入 in-doubt,只能等待 → 阻塞
直观类比
婚礼司仪问双方"愿意吗"(prepare,双方承诺后不许反悔);司仪宣布"结为夫妻"(commit)。司仪说完第一阶段就晕倒了:新郎新娘(参与者)既不能私自结婚也不能私自散伙——只能原地等司仪醒来(阻塞)。
实例或案例
- XA / JTA:Java 生态跨库事务标准,金融存量系统仍在用;运维痛点正是 in-doubt 事务清理。
- MySQL XA:
XA START/PREPARE/COMMIT;与 binlog 复制互动的细节曾长期是坑。 - Kafka 事务:本质是"向事务协调者 + 多分区日志"的 2PC 变体,配合幂等生产者实现跨分区原子写。
- 跨库扣款:传统方案 XA;互联网方案多为 Saga/TCC(kp-024)以换可用性。
常见误区
- 误区一:"2PC 是容错的"。它只保证原子性语义(要么都成要么都不成),不保证可用性——协调者崩溃即阻塞;容错版需要共识加持。
- 误区二:"3PC 解决了 2PC 的问题"。3PC 在网络分区下会脑裂(两侧各自推进),用不一致风险换阻塞减少,得不偿失,生产几乎不用。
- 误区三:"同步链路里加 XA 没代价"。prepare 阶段锁资源直到 commit,长事务会把锁持有时间放大数倍,吞吐骤降。
与其他知识点的关系
- kp-017/019:共识如何为提交决定加容错(原子提交 = 2PC + 共识)。
- kp-024:以补偿换可用性的柔性事务谱系。
- kp-027:Spanner 将 2PC 与 Paxos、TrueTime 组合的全局事务实现。
自测题
- 参与者投 yes 后协调者失联,它为什么不能自行决定?
答:它不知道其他参与者收到的是 commit 还是 abort;自行提交可能与其他 abort 分支矛盾,自行中止则违背承诺,任何单方动作都可能破坏原子性,只能等待。
- 2PC 与 Paxos 都两阶段,本质差别?
答:Paxos 的多数派让新 leader 可重建决定(无阻塞),2PC 的决定权收敛在协调者且参与者无更替能力(会阻塞);容错原子提交需把 2PC 的每步决定放上共识。
- 3PC 为什么被淘汰?
答:为消除阻塞引入 pre-commit 与超时默认,但分区时两个分支可分别推进到提交与中止,引入不一致风险,且多一轮延迟,收益不抵代价。
延伸阅读
- Jim Gray, "Notes on Data Base Operating Systems"(1978,2PC 原始表述)。
- Philip Bernstein 等《Principles of Transaction Processing》。
- Spanner 论文对"2PC + Paxos"组合的论述(kp-032 延伸)。