某银行跨行转账场景:从 A 银行扣款、向 B 银行入账,两个操作必须同时成功或同时失败。如果 A 扣款成功但 B 入账失败,资金就会凭空消失。这就是分布式事务的经典问题——如何保证跨节点操作的原子性。2PC(两阶段提交)和 3PC(三阶段提交)是解决这一问题的协议基础。
一、分布式事务的原子性问题
单机事务由数据库通过 WAL(Write-Ahead Logging)保证原子性,但跨节点时没有单一协调者可以依赖。
分布式事务需要保证:
- 原子性:所有参与者要么全部提交,要么全部回滚
- 一致性:事务前后数据满足完整性约束
- 隔离性:并发事务互不干扰
- 持久性:提交后的结果不会丢失
2PC 和 3PC 主要解决原子性问题。
二、两阶段提交(2PC)
2.1 协议流程
2PC 引入一个 协调者(Coordinator)角色,分两阶段完成提交:
阶段一:准备阶段(Prepare)
- 协调者向所有参与者发送 Prepare 请求
- 参与者执行事务操作,写入 Undo/Redo 日志,但不提交
- 参与者返回 Yes(准备就绪)或 No(无法提交)
阶段二:提交阶段(Commit)
- 如果所有参与者返回 Yes:协调者发送 Commit
- 如果任一参与者返回 No 或超时:协调者发送 Abort
2.2 参与者状态机
2.3 故障分析
2PC 在各种故障场景下的行为:
| 故障场景 | 影响 | 处理方式 |
|---|---|---|
| 参与者 Prepare 前崩溃 | 不影响 | 协调者超时后 Abort |
| 参与者 Prepare 后崩溃 | 阻塞 | 恢复后根据日志决定 |
| 协调者 Prepare 后崩溃 | 阻塞 | 参与者无限等待 |
| 网络分区 | 阻塞 | 多数派无法通信 |
2.4 2PC 的核心问题:同步阻塞
2PC 最大的问题是同步阻塞:
- 参与者在 Prepare 后必须锁定资源,等待协调者的最终决定
- 如果协调者崩溃,参与者将无限期阻塞
- 这种阻塞可能导致整个系统资源耗尽
时间线:t1: 参与者 Prepare 成功,锁定资源t2: 协调者崩溃t3: 参与者等待...资源被锁定...其他事务被阻塞...t4: 协调者恢复,发送 Commit/Abort2PC 的阻塞问题本质上是单点故障问题。协调者是单点,其崩溃会导致所有参与者阻塞。即使协调者恢复,如果它丢失了 Prepare 阶段的决策信息,也无法做出正确的提交决定。
三、三阶段提交(3PC)
3.1 协议改进
3PC 将 2PC 的提交阶段拆分为两个阶段,增加超时机制:
阶段一:CanCommit
协调者询问参与者是否”可能”提交,参与者只做可行性检查,不锁定资源。
阶段二:PreCommit
协调者发送 PreCommit,参与者执行事务操作并锁定资源,但不提交。
阶段三:DoCommit
协调者发送 DoCommit,参与者正式提交。
3.2 超时机制
3PC 的关键改进是参与者的超时机制:
- 如果参与者在 PreCommit 状态等待 DoCommit 超时,自动提交
- 因为到达 PreCommit 说明所有参与者都同意了 CanCommit,提交是安全的
参与者超时逻辑:- 等待 PreCommit 超时 → Abort(可能其他参与者拒绝了 CanCommit)- 等待 DoCommit 超时 → Commit(所有参与者已同意,提交是安全的)3.3 3PC 的局限
3PC 解决了协调者崩溃后的阻塞问题,但仍有局限:
- 网络分区时仍可能出现数据不一致
- 如果参与者在 PreCommit 后崩溃,恢复后可能与其他参与者决定不同
- 额外的通信轮次增加了延迟
- 工程实现复杂度高,实际使用较少
3PC 假设网络延迟有界(同步网络模型),但在实际异步网络中,超时机制无法完全避免不一致。因此 3PC 更多是理论上的改进,工程实践中很少直接使用。
四、2PC 的工程实现:XA 协议
XA 协议是 2PC 的工业标准实现,由 X/Open 组织定义。
4.1 核心组件
| 组件 | 角色 | 对应 2PC |
|---|---|---|
| AP(Application Program) | 应用程序 | 发起事务 |
| TM(Transaction Manager) | 事务管理器 | 协调者 |
| RM(Resource Manager) | 资源管理器 | 参与者 |
| CRM(Communication Resource Manager) | 通信资源管理器 | 节点间通信 |
4.2 MySQL XA 事务
-- 开始 XA 事务XA START 'txn1';UPDATE account SET balance = balance - 100 WHERE id = 1;XA END 'txn1';
-- 第一阶段:PrepareXA PREPARE 'txn1';
-- 第二阶段:CommitXA COMMIT 'txn1';
-- 或回滚-- XA ROLLBACK 'txn1';4.3 XA 的问题
| 问题 | 描述 | 影响 |
|---|---|---|
| 同步阻塞 | Prepare 后资源锁定 | 性能下降 |
| 单点故障 | TM 崩溃导致阻塞 | 可用性降低 |
| 日志开销 | Undo/Redo 日志写入 | 延迟增加 |
| 跨数据库兼容 | 不同数据库 XA 实现差异 | 运维复杂 |
五、2PC/3PC 与其他方案的对比
| 维度 | 2PC | 3PC | TCC | Saga |
|---|---|---|---|---|
| 一致性 | 强一致 | 强一致 | 最终一致 | 最终一致 |
| 阻塞 | 是 | 减轻 | 否 | 否 |
| 性能 | 低 | 较低 | 中 | 高 |
| 实现复杂度 | 低 | 高 | 中 | 中 |
| 业务侵入 | 无 | 无 | 高 | 中 |
| 适用场景 | 短事务 | 理论研究 | 电商交易 | 长流程 |
六、实践建议
- 优先使用数据库本地事务:如果可以避免分布式事务,就不要引入
- 2PC 适用于短事务:资源锁定时间短,阻塞影响可控
- 长事务考虑 TCC/Saga:避免长时间锁定资源
- XA 协议注意超时配置:合理的超时可以减少阻塞影响
- 监控悬挂事务:Prepare 后未 Commit/Abort 的事务需要定期清理
-- MySQL 查看悬挂的 XA 事务XA RECOVER;2PC 是分布式事务的协议基础,理解其原理和局限是选择更优方案的前提。3PC 虽然在理论上改进了阻塞问题,但在异步网络模型下仍不完美,工程实践中更多采用 TCC 和 Saga 等最终一致性方案。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






