分布式事务总览:Seata 的 AT/TCC/SAGA 模式
微服务拆分后,一个业务动作往往要跨多个服务、操作多个数据库:下单要同时扣库存(库存库)、减账户(账户库)、记订单(订单库)。单个服务里的事务只能保证自己那个库,库存扣了、订单没写成怎么办?这就是分布式事务要解决的问题。Seata 是当前 Spring Cloud 生态最主流的一站式方案,本章先讲清楚它的模型与四种模式,下一章实战 AT。
为什么本地事务解决不了
本地事务(@Transactional)的原子性以单个数据源为边界。跨库跨服务时,没有共同的"undo 日志"与锁管理器,各库各自提交,无法保证要么全成要么全败。想让多个独立数据源达成一致,需要引入一个协调者统一决策——这正是分布式事务与本地事务的本质区别。
Seata 的三个角色
理解 Seata 先记三个缩写:TC(事务协调器,独立部署的 seata-server,负责全局事务的开启、提交与回滚决策);TM(事务管理器,业务发起方,用注解声明全局事务边界,向 TC 请求全局提交/回滚);RM(资源管理器,各参与服务,管理自己数据源上的分支事务并向 TC 注册汇报)。一次全局事务的简化流程:TM 向 TC 开启全局事务 → 各参与方执行本地事务并注册分支 → 全部成功则 TC 通知全局提交,任一分支失败则 TC 通知各分支回滚。
四种模式速览
Seata 提供 AT、TCC、SAGA、XA 四种模式,覆盖从"无侵入"到"强一致"的不同诉求:
- AT 模式:基于本地事务 + undo_log 自动生成反向补偿 SQL,业务代码零侵入(仍需建 undo_log 表),适合"关系型数据库 + 常规 CRUD"的主流场景,代价是全局锁带来一定吞吐损耗;
- TCC 模式:业务方自己实现 Try(预留资源)、Confirm(确认)、Cancel(补偿取消)三段逻辑,无锁、性能好,但需要处理空回滚、悬挂、幂等等问题,改造量大;
- SAGA 模式:把长事务拆成有序子事务,失败时按逆序执行补偿,适合跨系统、无统一数据库的长流程编排,一致性偏最终一致;
- XA 模式:直接利用数据库的 XA 协议做两阶段提交,强一致、侵入小,但要求数据库支持且资源被事务持有期间锁开销大。
怎么选:一张决策表
| 模式 | 侵入性 | 一致性 | 典型场景 |
|---|---|---|---|
| AT | 低(自动补偿) | 较强(全局锁) | 常规 CRUD、多库更新 |
| TCC | 高(三段业务逻辑) | 强 | 高并发、资源预留类业务 |
| SAGA | 中(状态机/编排) | 最终一致 | 长流程、跨异构系统 |
| XA | 低 | 强 | 库支持好、并发要求不高 |
版本与形态说明
Seata 已是 Apache 基金会项目,写作时主流为 2.x(如 2.3.x),客户端与服务端版本需匹配,部署与配置方式随版本演进较大,一律以官方文档为准。Seata 与 Spring Cloud Alibaba 集成后,通过 seata-spring-boot-starter 接入,事务入口用 @GlobalTransactional 声明(第 18 章)。
一次全局事务的典型时序
以"下单同时扣库存、扣账户"为例,把三角色的动作串成时间线:
- 入口服务 order-service 作为 TM,向 TC 开启全局事务,拿到全局事务编号 XID;
- order-service 执行本地事务写入订单并提交,作为 RM 向 TC 注册第一个分支;
- XID 随 Feign 调用透传给库存、账户服务(与第 22 章讲的上下文传递是同一机制),它们各自完成本地事务并注册分支;
- 所有分支成功时,TM 请求 TC 全局提交,各 RM 完成最终确认;
- 任一分支失败时,TM 请求 TC 全局回滚,已提交的分支按各自模式撤销:AT 回放 undo_log、TCC 执行 Cancel、SAGA 按序补偿。
事务边界设计三原则
- 收敛入口:一个业务流程只由一个服务开启全局事务,通常是流程编排方,避免多个服务各自开全局事务互相嵌套;
- 只包写路径:纯查询与可异步化的动作留在全局事务之外,事务范围越大,全局锁与协调开销越大;
- 事务内不做慢事:外部 API、大循环、等待用户确认等耗时操作不进事务,它们会拉长全局锁持有时间、放大分支冲突。
常见误区
把分布式事务引入生产前,先避开三个高频误区:
- 把 @GlobalTransactional 当 @Transactional 随处乱加:事务范围越大,全局锁与协调开销越大,应只包住真正需要强一致的那段写路径;
- 忽略非数据库副作用:AT 的回滚依赖 undo_log,业务里"发消息、调外部 API"这类动作不会被自动撤销,需要额外的消息补偿或对账兜底;
- 只测成功路径:分布式事务的故障大多发生在"第 N 个分支失败"时,演练回滚比演练提交更重要,也要验证半途宕机后的恢复。
先想清楚:真的需要分布式事务吗
分布式事务有成本(协调开销、锁等待、运维复杂度),能避免就避免。常见替代思路:调整业务边界把强相关操作收敛到一个服务一个库;用本地消息表或事件驱动 + 对账做最终一致;先落订单、异步补偿库存失败。只有业务确实要求"要么全成要么全败"且无法重构时,再引入 Seata。选型顺序建议:AT 优先,遇到性能瓶颈再评估 TCC,长流程编排考虑 SAGA。
学习路径建议
模式很多,不必一次学全,推荐按如下顺序推进:
- 先用 AT 做通一个最小闭环(第 18 章):部署 Server、建 undo_log、加注解、演练回滚,建立对 TC/TM/RM 的体感;
- 用故障演练验证补偿:人为让分支失败、模拟半途宕机,确认数据最终回到一致状态;
- 当吞吐成为瓶颈时再评估 TCC:先复现"全局锁争抢"的压测数据,用数据驱动选型,而不是提前引入复杂度;
- 长流程、跨异构系统的场景单独调研 SAGA,通常配合事件驱动一起设计。
小结
Seata 用 TC/TM/RM 三角色把多个数据源纳入同一个全局事务决策:AT 自动补偿最省事,TCC 换性能、SAGA 换长流程、XA 换强一致。动手之前先反问"这笔业务能不能不做成分布式事务"。确认需要后,下一章完成 AT 模式的实战接入。