4、三阶段提交(3PC)详解:3PC对2PC的改进、3PC执行流程、3PC的优缺点分析

聊完2PC,咱们来看看它的升级版——三阶段提交(3PC)。

说实话,我在早期做分布式事务时,第一反应就是用2PC。但踩过几次坑之后,我意识到2PC在某些场景下确实不够用。比如协调者挂了,参与者就一直卡着资源,这谁受得了?

3PC就是为解决这个问题来的。它把2PC的“二阶段”拆成了“三阶段”,多了一个预提交阶段。你想想看,多一个阶段,就多了一次沟通机会,容错能力自然就上来了。

3PC对2PC的改进

3PC主要改进了两个痛点:

  • 解决了单点阻塞问题:2PC中协调者挂了,参与者只能傻等。3PC引入了超时机制,参与者超时后可以自行处理。
  • 降低了数据不一致风险:多了一个预提交阶段,相当于给参与者一个“缓冲期”,提前确认状态。

我记得有一次线上事故,协调者节点突然宕机,2PC的参与者们锁了整整20分钟的资源。后来换成3PC,同样的情况,参与者超时后自动回滚,业务影响小了很多。

核心改进点:3PC把2PC的“要么全做,要么全不做”变成了“先问问大家行不行,再让大家准备一下,最后再执行”。每一步都有超时兜底。

3PC执行流程

3PC的三个阶段分别是:CanCommit、PreCommit、DoCommit。咱们一个一个说。

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

协调者问所有参与者:“你们能提交吗?”

参与者检查自身状态,比如资源是否充足、事务是否可执行。能就返回Yes,不能就返回No。

这个阶段不锁定资源,只是问问情况。我习惯把它叫做“打招呼阶段”。

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

如果所有参与者都返回Yes,协调者就发PreCommit指令。

参与者收到后,开始执行事务操作,但先不提交。比如写入undo/redo日志,把数据准备好。

这里要注意:参与者会启动超时计时器。如果超时没收到DoCommit或Abort指令,参与者会自行提交(不是回滚)。

避坑指南:我曾经遇到过参与者预提交后,网络分区导致收不到协调者指令。3PC的默认行为是提交,这在某些场景下可能造成数据不一致。所以,如果你的业务对一致性要求极高,3PC不一定是最佳选择。

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

协调者确认所有参与者都预提交成功了,就发DoCommit指令。

参与者收到后,正式提交事务,释放资源。

如果协调者发现某个参与者预提交失败,就发Abort指令,大家回滚。

整个流程可以用表格总结一下:

阶段 协调者动作 参与者动作 超时处理
CanCommit 询问能否提交 检查状态,返回Yes/No 协调者超时则中止
PreCommit 发送预提交指令 执行事务,写日志,返回ACK 参与者超时则自行提交
DoCommit 发送提交/中止指令 提交或回滚 参与者超时则自行提交

3PC的优缺点分析

3PC不是银弹,它有明显的优点,也有绕不开的缺点。

优点

  • 非阻塞性:参与者有超时机制,不会无限等待协调者。这一点比2PC强太多。
  • 容错性更好:协调者挂了,参与者能自行决策,系统不会卡死。
  • 数据一致性更高:预提交阶段提前暴露问题,减少了提交阶段失败的概率。

缺点

  • 性能开销更大:多了一次网络通信,延迟更高。说白了,3PC比2PC慢。
  • 脑裂风险:参与者超时后自行提交,如果协调者后来发Abort,就冲突了。我遇到过这种情况,最后靠人工介入才解决。
  • 实现复杂:超时时间、状态机、异常处理,代码量比2PC多不少。

我的建议:如果你的系统对一致性要求极高,且网络环境稳定,2PC其实够用。如果网络不稳定,或者你不想被协调者拖死,3PC是更好的选择。但要做好脑裂的预案。

嗯,3PC就聊到这儿。说白了,它是对2PC的一次重要改进,但也不是完美的。分布式系统里没有银弹,只有权衡。下一章咱们聊聊TCC,那又是另一种思路了。