分布式事务:2PC 与柔性事务

kp-018 · 事务与并发控制进阶约 30 分钟已校对

前置知识点

前置:

相关知识点

相关:

学习进度:

一句话定义

两阶段提交(2PC,Two-Phase Commit)用「准备投票 + 全局决定」让跨节点事务原子提交,但存在协调者单点阻塞问题;柔性事务(TCC、SAGA、消息最终一致)则放弃强原子性,用补偿与重试换取可用性与吞吐。

为什么重要

前置知识

kp-017(本地事务与提交语义)、建议先了解 kp-023 的复制概念。

核心概念

原理与机制

2PC 的正确性来自「投票点」:一旦全部参与者表示 Prepare 成功,事务必定可提交(每个参与者已把 Undo/Redo 持久化,之后无论谁崩溃都能恢复);一旦有人投 No,必定可中止。代价有两个:其一是阻塞——协调者在阶段二前崩溃,参与者抱着锁进入 in-doubt 状态直到协调者恢复,这是 2PC 最著名的弱点;其二是延迟——至少两轮同步日志刷盘往返,把单机事务的毫秒级放大到跨机房百毫秒级。柔性事务把原子性降级为「最终一致」:SAGA 的每步是本地事务,失败时逆序执行补偿(订金已扣则退款),补偿本身必须幂等且可重试;消息最终一致用「本地消息表 + 投递重试」把跨服务写变成可靠的异步事件。三者的选择矩阵:强一致短事务用 2PC/XA;可补偿的业务流程用 SAGA;资源预留语义清晰(冻结额度)用 TCC。

实例或案例

跨「订单库」与「账户库」扣款下单。2PC 方案:应用作为协调者,两库 Prepare(锁住金额行与库存行)→ 全 Yes 则双双 Commit;协调者宕机时两库行锁悬挂,直到恢复。SAGA 方案:本地事务一「创建订单(状态待支付)」,发消息触发本地事务二「扣款」,扣款失败则执行补偿「订单置为已取消」——任一时刻系统可见状态不一致(订单存在但未扣款),但最终收敛;每条消息带唯一业务单号做幂等键,防止重试导致重复扣款。

常见误区

自测题

  1. 2PC 中参与者投出 Yes 后协调者失联,参与者处于什么状态? 答:in-doubt(不确定)状态:既不能提交也不能回滚,必须持有锁等待协调者恢复后读取决定。
  2. SAGA 为什么要求每步可补偿且幂等? 答:补偿是失败路径的逆序回放,可能被重试多次;不可补偿的步骤会留下无法回退的半成品状态。

公式或模型

2PC 提交延迟 ≈ 2 × RTT + 2 × fsync(两轮消息 + 两轮刷盘);跨机房 RTT 5ms、fsync 1ms 时约 12ms 起步,叠加锁等待后远高于单机事务。

图示

协调者 参与者 A 参与者 B 1 prepare? 1 prepare? 2 yes 2 yes 3 commit 3 commit
2PC 时序:两轮往返,决定点后必达;任一环节失联即产生 in-doubt 悬挂

直观类比

2PC 像表决重要动议:主席先问所有人「想好了吗」(Prepare,想好即锁死自己的答案),全员「想好」才宣布通过(Commit),只要有一个人没想好就整体作废——主席中途离场,全员只能举着手干等。SAGA 则像装修队:每个工序先干完,出问题就按反序「拆掉重来」。

与其他知识点的关系

kp-014 的 ACID 是它的单机版本;kp-026 的 Raft 解决的正是 2PC 协调者单点问题(复制协调者本身);kp-024 的分片策略决定哪些事务被迫跨节点。

延伸阅读