2. 两阶段提交(2PC):分布式事务的“老大哥”
说到分布式事务,2PC 绝对是个绕不开的话题。我刚开始接触分布式系统时,第一个学的就是它。说白了,2PC 就是一套“大家商量着来”的协议——要么都提交,要么都回滚。
你想想看,在单机数据库里,事务靠的是本地锁和日志。但到了分布式环境,数据散落在不同节点上,怎么保证大家动作一致?2PC 给出的方案是:找个“协调者”来统一指挥。
核心思想:所有参与者先“预备”,确认自己能不能干。等所有人都说“能”,协调者再下令“开干”。只要有一个说“不行”,就全员回滚。
2.1 协议详解:两个阶段,五步走
2PC 的名字很直白——就两个阶段。我习惯叫它“投票阶段”和“执行阶段”。
第一阶段:投票(Voting Phase)
- 协调者发请求:问所有参与者,“我要提交事务了,你们准备好了吗?”
- 参与者写日志:每个参与者把事务写到 undo/redo 日志里,但先不提交。
- 参与者投票:能提交就回“Yes”,不能就回“No”。
第二阶段:执行(Commit Phase)
- 协调者做决策:如果所有人都说“Yes”,就发“Commit”;只要有“No”,就发“Abort”。
- 参与者执行:收到指令后,提交或回滚事务,然后回复“Ack”。
嗯,这里要注意:协调者必须收到所有参与者的 Ack,才能认为事务结束了。我在项目中就遇到过,有人以为发完 Commit 就完事了,结果丢了一堆确认消息。
2.2 角色分工:谁说了算?
| 角色 | 职责 | 我见过的坑 |
|---|---|---|
| 协调者(Coordinator) | 发起投票、收集结果、做最终决策 | 单点故障,挂了就全完 |
| 参与者(Participant) | 执行本地事务、投票、响应指令 | 资源被锁住,别人进不来 |
协调者就像项目经理,参与者就是各个开发。项目经理问“这功能周五能上线吗?”——大家说“能”,那就上线;有人说“不能”,那就延期。道理一模一样。
2.3 同步阻塞问题:最让人头疼的地方
2PC 最大的毛病,就是同步阻塞。什么意思?
参与者一旦投了“Yes”,就必须把资源锁住,等着协调者的最终指令。这期间,其他事务想访问同一份数据?对不起,排队等着。
我曾经踩过的坑:在一个高并发订单系统里用了 2PC。结果某个参与者网络延迟,锁了订单表 30 秒。那 30 秒里,所有下单请求都堵死了。用户疯狂投诉,老板差点把我叫去喝茶。
为什么会这样?因为 2PC 的设计就是“要么不做,要么做到底”。参与者不敢擅自释放锁,万一协调者说“提交”,你锁都放了,数据就乱了。
说白了,这是用性能换一致性。在低并发场景下还能忍,高并发下基本就是灾难。
2.4 单点故障风险:协调者挂了怎么办?
协调者是 2PC 的“大脑”。大脑一死,整个系统就瘫痪了。
分两种情况看:
- 第一阶段挂了:参与者收不到投票请求,事务自然就超时回滚了。问题不大。
- 第二阶段挂了:这才是噩梦。参与者已经投了“Yes”,锁着资源等指令。协调者一死,没人告诉它们该提交还是回滚。它们只能一直等,等到天荒地老。
我的建议:生产环境一定要给协调者做高可用。比如用 ZooKeeper 选主,或者搞个备用协调者。别问我怎么知道的——我第一版 2PC 实现就没做,结果凌晨三点被电话叫醒。
还有个办法:参与者设置超时机制。等太久没收到指令,就主动询问协调者。但这也只能缓解,不能根治。
2.5 核心逻辑流程图
下面这张图,我画了 2PC 的完整流程。你看一眼就明白了。
2.6 总结一下
2PC 的优点很明确:实现简单,强一致性。在节点少、网络稳定的场景下,它工作得很好。
但缺点也致命:
- 同步阻塞:锁资源,影响并发
- 单点故障:协调者一挂,全员卡死
- 脑裂风险:网络分区时,协调者可能做出错误决策
我个人习惯把 2PC 用在内部系统、低并发、对一致性要求极高的场景。比如配置中心同步、批量数据迁移。至于高并发交易系统?嗯,后面我们会聊到更好的方案。
避坑指南:如果你非要用 2PC,记住三点——1)给协调者做高可用;2)参与者设置合理的超时时间;3)监控锁等待时长,超过阈值就报警。我曾经因为没做第三点,半夜被运维电话吵醒三次。
公众号:蓝海资料掘金营,微信deep3321