3PC 三阶段提交

面试官问候选人:"3PC 是怎么改进 2PC 的阻塞问题的?"

候选人说:"3PC 多了一个阶段..."

面试官追问:"具体多的是哪个阶段?为什么能减少阻塞?"

候选人答不上来。

面试官继续问:"那 3PC 就没有阻塞问题了吗?"

候选人彻底卡住。

【架构权衡】 3PC 是 2PC 的理论改进,通过增加"预提交"阶段来减少阻塞。但在生产环境中,3PC 很少被使用。原因在于:它增加了网络往返次数(延迟更高),而且在极端网络分区情况下仍可能不一致。理解 3PC 的原理和局限性,才能正确判断何时该用它。


一、核心问题 🔴

1.1 2PC 的问题 vs 3PC 的改进

2PC 的问题:
├─ 阻塞时间长:参与者锁定资源直到事务结束
├─ 协调者单点:协调者崩溃后参与者永久等待
└─ 无法处理协调者崩溃:参与者不知道该提交还是回滚

3PC 的改进:
├─ 减少阻塞:资源锁定时间缩短
├─ 协调者崩溃影响小:参与者可以主动询问
└─ 增加预提交阶段:让参与者知道应该等待还是放弃

1.2 3PC 的三个阶段

┌─────────────────────────────────────────────────────────────────┐
│                       阶段一:CanCommit                           │
│                                                                  │
│  协调者 → 所有参与者:Can you commit?                            │
│  参与者 → 协调者:Yes / No                                       │
│                                                                  │
│  目的:确认所有参与者是否可以提交                                │
│  └─ 如果任何一个参与者返回 No,整个事务终止                      │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                      阶段二:PreCommit                           │
│                                                                  │
│  协调者 → 所有参与者:PreCommit                                  │
│  参与者收到后:                                                 │
│    ├─ 记录 PreCommit 状态                                       │
│    ├─ 锁定资源(但不提交)                                      │
│    └─ 回复 ACK                                                 │
│                                                                  │
│  参与者超时未收到 PreCommit:                                    │
│    └─ 主动 abort(因为如果有人返回 No,协调者会发 Abort)       │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                       阶段三:DoCommit                           │
│                                                                  │
│  协调者 → 所有参与者:DoCommit                                   │
│  参与者收到后:                                                 │
│    ├─ 正式提交                                                 │
│    └─ 释放锁                                                   │
│                                                                  │
│  参与者超时未收到 DoCommit:                                     │
│    └─ 主动 commit(因为已收到 PreCommit,说明大家都准备好了)   │
└─────────────────────────────────────────────────────────────────┘

二、核心改进

2.1 减少阻塞时间

2PC vs 3PC 阻塞时间对比:

2PC:
├─ 锁定资源:PREPARE → COMMIT/ROLLBACK
└─ 阻塞时间:整个事务持续时间

3PC:
├─ 锁定资源:PreCommit → DoCommit
└─ 阻塞时间:缩短到最后一个阶段的持续时间

好处:
└─ 如果 DoCommit 阶段延迟,参与者不会永久阻塞

2.2 协调者崩溃影响

2PC:协调者崩溃在 PREPARE 之后
├─ 参与者不知道该提交还是回滚
└─ 必须等待协调者恢复

3PC:协调者崩溃在 PreCommit 之后
├─ 参与者已收到 PreCommit,知道所有人都准备好了
├─ 如果 DoCommit 超时,参与者可以主动 commit
└─ 不需要等待协调者恢复

三、缺陷分析

3.1 3PC 的固有问题

问题1:网络分区下的数据不一致

场景:协调者和部分参与者在分区一侧

分区前:
├─ 协调者发送 PreCommit 给参与者1、2
├─ 参与者1、2 收到并回复 ACK
├─ 参与者3 未收到 PreCommit,超时后主动 Abort
├─ 网络分区发生
└─ 分区一侧:协调者 + 参与者1、2(收到 PreCommit)
    分区另一侧:参与者3(未收到 PreCommit,已 Abort)

分区后:
├─ 协调者 + 参与者1、2 执行 DoCommit → 提交
├─ 参与者3 执行本地 Abort → 回滚
└─ 结果:数据不一致
问题2:异步消息导致的不一致

参与者收到 PreCommit 后,网络分区
参与者无法确定:是"应该等 DoCommit"还是"应该 Abort"

选择1:等 DoCommit(如果协调者在另一分区,不等)
选择2:执行 Abort(如果协调者在这一分区,已发送 DoCommit)

无论选择哪个,都可能导致不一致

3.2 3PC 的延迟问题

3PC vs 2PC 延迟对比:

2PC(无分区):
├─ PREPARE:RTT(Round Trip Time)
├─ COMMIT:RTT
└─ 总体:2 × RTT

3PC(无分区):
├─ CanCommit:RTT
├─ PreCommit:RTT
├─ DoCommit:RTT
└─ 总体:3 × RTT

结论:3PC 的延迟比 2PC 高 50%

【架构权衡】 3PC 减少了 2PC 的阻塞问题,但引入了新的问题:

  1. 延迟更高(3 × RTT vs 2 × RTT)
  2. 在极端网络分区下仍可能不一致
  3. 实现更复杂

因此,在大多数生产环境中,团队更倾向于选择 TCC 或 Saga 等补偿型方案,而不是 3PC。

四、工程代价评估

维度2PC3PCTCC
阻塞时间
网络开销
实现复杂度
一致性保证最终一致
延迟更高
数据不一致风险可控
生产使用率很低

五、面试总结

3PC 是 2PC 的理论改进,通过增加预提交阶段减少了阻塞时间。但它不是银弹:

  • 优势:减少阻塞时间,协调者崩溃影响较小
  • 劣势:延迟更高,复杂网络分区下仍可能不一致
  • 现实:生产环境中很少使用,TCC 和 Saga 更常见

理解 3PC 的原理和局限,才能正确评估何时该用它、何时该选替代方案。