{T}

分布式事务有哪些解决方案?

0. 引言

微服务拆分后,一个业务操作往往跨多个服务、多个数据库,"要么全成功、要么全失败"的本地事务能力失效。分布式事务要解决的就是跨资源(库/服务)的原子性。业界方案众多:从经典的 2PC/3PC,到柔性事务的 TCC、Saga、本地消息表、MQ 事务消息,再到 Seata 的 AT 模式。本文先给全景图,再逐一拆解。

1. 方案全景与分类

图表渲染中…
方案一致性侵入性性能适用场景
2PC/XA强一致低(数据库原生支持)低(同步阻塞)跨库事务、短事务、低并发
3PC强一致(仍有缺陷)理论价值大于实践
TCC最终一致(三方法改造)资金类强管控场景(转账、扣款)
Saga最终一致中(补偿逻辑)长流程、异步化(订单全链路)
本地消息表最终一致无 MQ 事务能力的场景
MQ 事务消息最终一致RocketMQ 用户的最佳选择
Seata AT最终一致(全局读已提交)(自动生成回滚镜像)希望低成本接入的 Java 服务

2. 刚性事务:2PC / 3PC

2.1 两阶段提交(2PC)

引入事务协调者(Coordinator)资源管理器(RM)

图表渲染中…
  • 阶段一 Prepare:参与者执行事务但不提交,锁定资源,向协调者报告"就绪/中止";
  • 阶段二 Commit/Rollback:协调者根据投票结果统一提交或回滚;
  • 缺陷:同步阻塞(资源锁定期间其他事务被阻塞)、协调者单点(挂掉则参与者永久阻塞)、脑裂(Commit 消息丢失时参与者不知该提交还是回滚)。

2.2 三阶段提交(3PC)

在 2PC 前增加 CanCommit 询问阶段,并将 Commit 阶段改为超时自提交(参与者超时后默认提交):

  • 缓解了"协调者挂掉后参与者无限阻塞"的问题;
  • DoCommit 阶段网络分区时仍可能不一致(部分提交、部分回滚),且多一轮 RPC 性能更差——实践中极少使用,详见《对比两阶段提交,三阶段协议有哪些改进?》。

3. 柔性事务一:TCC

Try / Confirm / Cancel 三方法,业务层补偿:

方法阶段示例(转账)
Try(预留)冻结资源扣减账户余额到"冻结"状态
Confirm(确认)提交预留冻结转真实扣减
Cancel(取消)释放预留冻结回滚到余额
  • 优点:灵活、性能好于 2PC、可自定义补偿粒度;
  • 代价:业务侵入大(每个资源都要实现三方法),还要处理空回滚、幂等、悬挂三大问题(详见《如何在业务中体现 TCC 事务模型?》);
  • 代表框架:Seata TCC、ByteTCC、tcc-transaction。

4. 柔性事务二:Saga

长事务拆分为本地事务序列,每个本地事务配一个补偿事务

text
T1(扣库存) → T2(扣余额) → T3(创建订单) → 全部成功 = 完成
失败时反向补偿:C2(还余额) → C1(还库存)
  • 编排模式:Choreography(事件驱动)——各服务监听事件自行执行;Orchestration(编排器)——中央 Saga 对象协调;
  • 适用:长流程、异步化业务(订单、预订、审批流);不适用于对中间状态敏感的场景(补偿期间数据可见)。

5. 柔性事务三:本地消息表 + MQ 事务消息

两者思路一致:"本地事务 + 可靠消息"解耦,事务与消息同生共死,最终由消费方对账补偿。

  • 本地消息表:业务表与消息表同库同事务;定时任务扫描未发送消息 → 投递 MQ → 消费方处理后标记完成。经典但需自己维护消息表与任务;
  • MQ 事务消息(RocketMQ):生产者先发 half 消息(消费者不可见)→ 执行本地事务 → 提交/回滚 half 消息;超时未决时由 Broker 回查生产者。把消息表下沉到 MQ 内部,业务零侵入。
图表渲染中…

6. Seata AT 模式:低成本柔性事务

  • 一阶段:业务 SQL + 自动生成 undo_log(回滚镜像),同库事务提交;
  • 二阶段:全局提交 = 删除 undo_log;全局回滚 = 用 undo_log 反向补偿生成 UPDATE 语句执行;
  • 无需业务写 Try/Confirm/Cancel,Java 注解 @GlobalTransactional 即可
  • 依赖全局锁(写隔离),读已提交隔离级别下对业务方透明。

7. 如何选型

text
跨库且强一致要求高、并发低 → 2PC/XA(或 Seata AT)
资金类强管控、可接受高侵入 → TCC
长流程、异步化、可补偿 → Saga
已有 RocketMQ、最终一致可接受 → 事务消息(首选,侵入最小)

通用原则:尽量不用分布式事务——通过"本地事务 + 消息 + 对账"把强一致降级为最终一致;能将操作收敛到单库单服务就绝不拆分。

8. 小结

  • 刚性事务(2PC/3PC)保证强一致但同步阻塞、性能差,仅低并发场景可用;
  • 柔性事务(TCC/Saga/消息)以"最终一致 + 补偿"换取高性能,是互联网主流;
  • TCC 重侵入Saga 适合长流程事务消息最省事Seata AT 是 Java 生态的折中
  • 架构上优先"避免分布式事务":本地事务 + 消息 + 对账兜底。

下一章对比 2PC 与 3PC:三阶段协议到底改进了什么、又留下了什么缺陷。