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,那又是另一种思路了。