分布式事务有哪些解决方案?
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:三阶段协议到底改进了什么、又留下了什么缺陷。