2. 强一致性方案之2PC:两阶段提交协议详解
聊到分布式事务,2PC 绝对是个绕不开的话题。我刚开始接触分布式系统时,第一个学的强一致性方案就是它。说白了,两阶段提交就是让多个节点在同一个事务里「要么全做,要么全不做」。
你想想看,在单机数据库里,我们靠本地事务和锁就能搞定一致性。但到了分布式环境,数据散落在不同机器上,怎么保证大家步调一致?2PC 就是最早被广泛使用的答案。
2.1 什么是两阶段提交?
两阶段提交(Two-Phase Commit,简称 2PC),是一种原子提交协议。它确保所有参与者节点要么全部提交事务,要么全部回滚。
整个协议分为两个阶段:
- 第一阶段:投票阶段(Voting Phase)——协调者问大家「能不能提交?」
- 第二阶段:执行阶段(Execution Phase)——协调者告诉大家「提交」或「回滚」
嗯,这里要注意:2PC 是一个阻塞协议。什么意思?就是参与者拿到锁之后,在第二阶段结束前,资源一直锁着。我在项目中遇到过因为 2PC 锁超时导致整个链路雪崩的情况,后面会细说。
核心思想:引入一个协调者(Coordinator),统一管理所有参与者(Participant)的事务生命周期。
2.2 协调者与参与者角色
2PC 里只有两种角色,但各自承担的责任完全不同。
协调者(Coordinator)
协调者是整个事务的「大脑」。我个人习惯把协调者设计成独立服务,而不是和业务服务混在一起。为什么呢?因为协调者挂了,整个事务就卡住了。
- 发起事务请求
- 收集投票结果
- 做出全局决策(提交/回滚)
- 处理超时和异常
参与者(Participant)
参与者是真正干活的人。每个参与者负责操作自己的本地资源,比如更新数据库、写消息队列。
- 执行本地事务(但不提交)
- 反馈投票结果(Yes/No)
- 根据协调者指令执行提交或回滚
我的经验:参与者在第一阶段执行完操作后,一定要把 undo/redo 日志写进本地磁盘。我曾经遇到参与者收到提交指令后突然宕机,重启后靠 redo 日志恢复了数据,才没丢数据。
2.3 2PC 的完整流程
光说概念太抽象,我们画个流程图看看。
从图上可以清楚看到:
- 协调者先给所有参与者发
prepare请求 - 每个参与者执行本地事务,写日志,但不提交
- 参与者回复 Yes 或 No
- 协调者收集所有回复:全 Yes 就发 commit,有 No 就发 rollback
- 参与者收到指令后执行提交或回滚
⚠️ 注意:第二阶段协调者发出 commit 后,如果某个参与者宕机了,它重启后必须根据本地日志自动完成提交。这就是为什么我前面强调要写 redo 日志。
2.4 2PC 的优缺点分析
任何方案都有两面性。2PC 看起来简单,但实际用起来坑不少。我结合自己的踩坑经历,给你梳理一下。
优点
- 原理简单,容易理解——相比 Paxos、Raft 这些协议,2PC 的逻辑非常直白
- 强一致性保障——要么全部提交,要么全部回滚,数据不会出现部分写入
- 实现成本低——很多中间件(如 Atomikos、Seata)都内置了 2PC 支持
- 兼容性好——只要支持 XA 协议的数据库都能直接接入
缺点
- 同步阻塞问题——参与者持有资源锁,直到第二阶段结束才释放。高并发下容易死锁
- 单点故障风险——协调者挂了,整个事务卡住。参与者只能一直等,或者超时回滚
- 数据不一致风险——第二阶段协调者发 commit 时自己宕机了,部分参与者收到 commit,部分没收到,数据就不一致了
- 性能瓶颈——两轮网络交互,延迟翻倍。我测过,跨机房场景下 2PC 的 TPS 比单机事务低 5-10 倍
| 维度 | 评价 | 说明 |
|---|---|---|
| 一致性 | ⭐⭐⭐⭐⭐ | 强一致性,但存在极端不一致风险 |
| 可用性 | ⭐⭐ | 协调者单点,参与者阻塞 |
| 性能 | ⭐⭐ | 两轮 RTT,锁持有时间长 |
| 实现复杂度 | ⭐⭐⭐ | 协议简单,但异常处理复杂 |
| 适用场景 | ⭐⭐⭐ | 短事务、低并发、强一致性要求 |
避坑指南:我曾经在一个支付系统中用 2PC 做账户扣款和积分发放。上线后发现,高峰期经常出现「参与者等待协调者超时」的问题。后来我们加了一个超时重试机制,并且把协调者做成了集群部署,才勉强稳住。说实话,如果对性能要求高,2PC 不是最佳选择。
2.5 什么时候该用 2PC?
我个人觉得,2PC 适合以下场景:
- 事务涉及的数据量小,执行时间短(毫秒级)
- 并发量不高,能接受锁等待
- 对一致性要求极高,不能接受最终一致性
- 参与者数量少(2-5 个),网络稳定
反过来,如果你的系统有高并发、长事务、跨机房部署这些特征,我建议你慎重考虑 2PC。后面章节我们会聊 TCC、Saga 这些方案,它们在某些场景下比 2PC 更合适。
一句话总结:2PC 是分布式事务的「入门方案」,理解它才能理解更复杂的协议。但生产环境慎用,除非你能接受它的阻塞和单点问题。
公众号:蓝海资料掘金营,微信deep3321