4. 分布式事务模式(一)

两阶段提交(Two-Phase Commit,2PC)是分布式事务中最经典的原子提交协议。在事件风暴画布中,当单个业务事件需要跨多个聚合(Aggregate)或多个限界上下文(Bounded Context)进行强一致的状态变更时,2PC 常被作为第一候选方案。本章将深入剖析 2PC 的核心流程、在事件风暴中的映射关系以及其固有的性能瓶颈与故障场景。

4.1 两阶段提交(2PC)核心原理

2PC 引入一个协调者(Coordinator)和多个参与者(Participant)。整个事务分为两个阶段:准备阶段(Voting Phase)和提交阶段(Commit Phase)。协调者负责收集所有参与者的投票,并根据投票结果决定全局提交或全局回滚。

核心角色定义:
  • 协调者(Coordinator): 通常由事务管理器(TM)承担,负责发起事务、记录日志、收集投票、做出全局决策。
  • 参与者(Participant): 通常由资源管理器(RM,如数据库、消息队列)承担,负责执行本地事务的预提交和最终提交/回滚。

4.1.1 正常流程时序

以下 SVG 展示了 2PC 在无故障情况下的标准交互流程:

2PC 正常流程时序图 应用/协调者 参与者 A 参与者 B 事务日志 阶段 1:准备 Prepare (事务ID + SQL) Prepare (事务ID + SQL) 写本地 UNDO/REDO 日志 写本地 UNDO/REDO 日志 Vote Yes Vote Yes 阶段 2:提交 写全局 Commit 日志 Global Commit Global Commit Commit ACK Commit ACK 写 End 日志,事务完成

4.2 2PC 在事件风暴画布中的映射

在事件风暴工作坊中,2PC 通常出现在以下场景:一个命令(Command)触发后,需要同时更新多个聚合(Aggregate),且业务规则要求这些更新全有或全无。此时,事件风暴画布上的元素与 2PC 组件存在如下映射关系:

事件风暴元素 2PC 组件 说明
命令(Command) 事务发起者 / 协调者入口 用户或外部系统发出的指令,如“创建订单并扣减库存”。
聚合(Aggregate) 参与者(Participant) 每个聚合实例对应一个资源管理器,负责本地数据的预提交和提交。
领域事件(Domain Event) 事务日志记录 在 2PC 中,协调者和参与者通过写日志来保证故障恢复能力;在事件风暴中,对应“订单已创建”、“库存已扣减”等事件。
限界上下文(Bounded Context) 分布式事务边界 2PC 通常跨越多个限界上下文(如订单上下文、库存上下文),协调者需要感知所有参与上下文。
策略(Policy) 超时与重试逻辑 当协调者等待投票超时,策略决定是重试还是回滚。
事件风暴实践提示: 在画布上标记 2PC 边界时,建议使用红色虚线框圈出所有参与强一致事务的聚合。如果虚线框跨越了多个限界上下文,说明该场景对分布式事务的依赖较高,需要重点评估 2PC 的可行性。

4.3 2PC 的优缺点分析

4.3.1 优点

  • 强一致性保障: 在所有参与者都同意提交的情况下,全局事务要么全部提交,要么全部回滚,不存在中间状态。
  • 协议成熟稳定: 2PC 是工业界最成熟的分布式事务协议,几乎所有主流数据库(MySQL、Oracle、PostgreSQL)和消息队列(RocketMQ、ActiveMQ)都原生支持 XA 协议(2PC 的一种实现)。
  • 实现相对简单: 相比于 TCC、Saga 等模式,2PC 对业务代码的侵入性较低,开发者只需声明事务边界,资源管理器自动完成预提交和提交。

4.3.2 缺点与故障场景

2PC 的缺点主要集中在性能和可用性两个方面。以下表格总结了核心问题:

问题类别 具体表现 故障场景描述
同步阻塞 参与者持有本地资源锁,等待协调者指令 在准备阶段,参与者对数据加锁(如行锁、页锁),直到收到全局 Commit 或 Rollback 才释放。如果协调者崩溃,参与者将长时间阻塞,导致系统吞吐量急剧下降。
单点故障 协调者成为系统瓶颈 如果协调者在准备阶段后、提交阶段前宕机,所有参与者将处于“悬而未决”状态,无法自行决定提交或回滚,必须等待协调者恢复。
数据不一致风险 部分参与者提交成功,部分失败 在提交阶段,如果协调者向参与者 A 发送了 Commit 指令并收到 ACK,但在向参与者 B 发送 Commit 时网络中断,会导致 A 已提交、B 未提交的不一致状态。
性能开销大 多次网络交互与磁盘 I/O 每个参与者至少需要 2 次网络往返(Prepare + Commit/Rollback),且每次都需要写事务日志(磁盘 fsync),在高并发场景下延迟显著增加。
故障排查重点: 在事件风暴画布中,如果发现某个聚合在 2PC 事务中长时间处于“等待”状态(对应领域事件迟迟未发出),应优先检查协调者的健康状态和事务日志。常见的故障根因包括:协调者进程 OOM、网络分区导致投票超时、参与者磁盘空间不足导致日志写入失败。

4.4 2PC 在事件风暴中的典型故障场景 SVG

以下 SVG 展示了 2PC 中最经典的“协调者崩溃导致参与者阻塞”故障场景:

2PC 故障场景:协调者崩溃导致参与者阻塞 协调者 参与者 A 参与者 B 故障点 1. 准备阶段(正常) Prepare Prepare Vote Yes Vote Yes 2. 协调者崩溃(故障) 宕机 3. 参与者阻塞(后果) 持有锁,等待 持有锁,等待 参与者 A 和 B 已写入 UNDO 日志并持有资源锁,但协调者崩溃,无法收到 Commit/Rollback 指令, 导致资源锁长时间不释放,后续请求被阻塞,系统吞吐量降为零。

4.5 2PC 在事件风暴中的适用性判断

在事件风暴画布上,建议使用以下标准判断是否应该采用 2PC:

  • 适用场景:
    • 业务要求强一致性,不允许出现中间状态(如金融转账、库存扣减与订单创建必须原子)。
    • 参与事务的聚合数量较少(通常 2~5 个),且每个聚合的操作耗时较短(毫秒级)。
    • 系统对吞吐量要求不高,但对数据一致性要求极高。
  • 不适用场景:
    • 参与事务的聚合数量多(超过 5 个),2PC 的阻塞时间会线性增长。
    • 系统要求高可用(99.99%+),协调者单点故障不可接受。
    • 业务允许最终一致性(如社交点赞、日志收集),此时 Saga 或 TCC 模式更优。
事件风暴画布决策建议: 当你在画布上识别出跨多个限界上下文的“红色虚线框”时,不要立即决定使用 2PC。先问团队两个问题:
1. 如果其中一个聚合在准备阶段后宕机 30 秒,业务能否接受?
2. 我们是否有能力实现协调者的高可用(如使用 ZooKeeper 或 etcd 选主)?
如果答案都是“否”,请优先考虑 Saga 或 TCC 模式。