3. 强一致性方案:3PC(三阶段提交)原理与阻塞问题破解

聊完2PC,很多同学会问:这玩意儿阻塞得这么厉害,有没有改进方案?

有,就是3PC。我当年第一次接触3PC时,第一反应是:多一个阶段就能解决阻塞?后来踩过坑才明白,事情没那么简单。

3.1 3PC的核心思想:引入超时机制

2PC最大的痛点是什么?是协调者挂了,参与者只能傻等。说白了,就是没有超时兜底。

3PC怎么改的?它把2PC的「提交请求」阶段拆成了两步:

  1. CanCommit:先问问大家能不能干
  2. PreCommit:告诉大家准备干
  3. DoCommit:正式开干

你想想看,多了一个阶段,就多了一次沟通机会。更重要的是——参与者也引入了超时机制

关键区别:2PC中只有协调者有超时,3PC中参与者也带超时。这意味着协调者挂了,参与者不会死等。

3.2 三阶段到底在做什么?

第一阶段:CanCommit(问询阶段)

协调者问所有参与者:「我要提交一个事务,你们能处理吗?」

参与者检查自身状态:资源够不够?有没有死锁?然后回复YES或NO。

这个阶段不执行任何业务逻辑,只是探路。我见过有人把业务逻辑放这里,结果第一阶段就超时了——嗯,这是典型的误用。

第二阶段:PreCommit(预提交阶段)

如果所有人都回复YES,协调者发PreCommit指令。

参与者收到后,开始执行事务操作,但不提交。数据写入undo/redo日志,资源锁定。

这里有个细节:参与者会启动一个超时计时器。如果超时没收到DoCommit或Abort,参与者会自动提交——这是3PC解决阻塞的关键设计。

我的经验:预提交阶段一定要做好幂等设计。我曾经遇到过网络抖动,PreCommit指令发了两次,结果参与者执行了两次预提交操作,资源锁了两遍。后来我们在预提交接口上加了个事务ID去重,才解决这个问题。

第三阶段:DoCommit(最终提交阶段)

协调者收集所有参与者的PreCommit响应:

  • 全部成功 → 发DoCommit,大家正式提交
  • 有失败或超时 → 发Abort,大家回滚

参与者收到DoCommit后提交事务,释放资源。如果超时没收到指令,参与者默认提交

注意:3PC的「默认提交」策略是一把双刃剑。如果网络分区导致参与者没收到Abort,它会自动提交——这就可能造成数据不一致。我曾经在一个金融项目中因为这个特性踩过坑,后面会细说。

3.3 3PC如何解决2PC的阻塞问题?

咱们对比着看:

场景 2PC行为 3PC行为
协调者挂了 参与者死等,资源一直锁着 参与者超时后自动提交或回滚
网络分区 参与者无法判断状态,持续阻塞 参与者根据超时策略自行决策
参与者挂了 协调者超时后回滚 协调者超时后回滚,其他参与者释放资源

说白了,3PC的核心改进就是:把「死等」变成了「超时后自主决策」

我记得有一次线上事故,2PC的协调者所在机器OOM了,所有参与者锁了20分钟的资源,业务直接瘫痪。后来换成3PC,同样的情况,参与者30秒超时后自动提交,虽然数据可能有点小问题,但至少业务没停。

3.4 3PC的代价:性能与一致性的权衡

3PC不是银弹。它解决了阻塞问题,但带来了新问题:

  • 多一次网络交互:3次RTT vs 2次RTT,延迟更高
  • 数据不一致风险:参与者超时后自动提交,可能和协调者的决策冲突
  • 实现复杂度翻倍:状态机更复杂,超时策略需要精细调优

我个人的习惯是:如果业务能接受短暂的不一致,优先考虑3PC。比如电商的库存扣减,偶尔多扣一点可以后续补偿,但系统不能卡死。

避坑指南:我曾经在一个支付系统中用3PC,结果因为网络抖动,参与者超时自动提交了,但协调者因为另一个参与者失败发了Abort。最后对账发现差了十几笔。后来我们加了「补偿事务」机制,才把这个坑填上。

3.5 什么时候该用3PC?

说实话,3PC在实际生产中用得不多。为什么?因为它的「自动提交」策略太不可控了。

但有些场景它确实合适:

  • 读多写少的场景:比如配置中心,大部分时间在读取,偶尔更新
  • 允许短暂不一致:比如用户积分系统,晚几分钟同步问题不大
  • 对可用性要求极高:系统不能因为协调者故障而完全不可用

嗯,这里要注意:如果你的业务要求强一致性,比如转账、订单状态变更,那3PC可能不是最佳选择。这时候我更推荐TCC或者Saga。

最后说一句:3PC是分布式事务发展史上的一个重要里程碑。它教会了我们一件事——在分布式系统里,超时和补偿比死等更靠谱。这个思想后来被广泛应用在TCC、Saga等方案中。