一、基础概念:TM、RM、TC
Seata 分布式事务核心三角色,定义缩写:
- TC (Transaction Coordinator,事务协调器)
全局事务中枢,独立服务,全局事务管家
- 记录全局事务状态、全局锁
- 接收 TM 开启 / 提交 / 回滚指令,通知所有 RM 分支提交或回滚
- 存储全局事务、分支事务日志(Seata Server 就是 TC)
-
TM (Transaction Manager,事务管理器)
全局事务发起方,通常是入口服务(Controller / 调用入口)
- 开启全局事务
@GlobalTransactional - 向 TC 申请创建全局事务、注册全局 ID (XID)
- 所有分支执行完后,决定全局提交 / 全局回滚,发给 TC
-
RM (Resource Manager,资源管理器)
分支事务参与者,每个数据库对应一个 RM
- 管理本地分支事务,向 TC 注册分支、上报分支执行状态
- 执行分支提交、分支回滚,处理全局锁、undo_log、行锁
- 每个微服务操作自己数据库时,就是一个 RM
XID:全局事务唯一 ID,贯穿整个分布式链路,所有分支都携带同一个 XID 关联全局事务。
二、AT 模式是什么?
AT(Automatic Transaction)自动事务模式 ,Seata 默认主流模式,无侵入、基于本地事务 + 补偿回滚 ,对业务代码改动极小,仅需注解 @GlobalTransactional。
核心底层原理:两阶段提交(2PC)改造版 ,利用undo_log(回滚日志)、global_lock(全局锁)保证最终一致性 ,隔离级别默认读未提交,可做防脏写。
AT 模式 第一阶段(准备阶段:执行业务 SQL,提交本地事务,预留回滚)
完整步骤:
- TM 开启全局事务,向 TC 请求生成全局 XID,绑定到当前调用链路。
- 微服务 RM 执行业务 DML(update/insert/delete)前,先查询当前数据快照 ,生成before_image(修改前数据镜像)。
- RM 执行业务 SQL 修改数据,生成after_image(修改后数据镜像)。
- 在同一个本地事务 内:
- 执行业务 SQL 更新业务表
- 插入一条
undo_log记录(存储 before/after 镜像、XID、主键) - 向 TC 注册当前分支事务
- 提交本地数据库事务,释放本地行锁,此时数据已经改到数据库,但并未全局生效。
- RM 向 TC 上报:本分支第一阶段执行成功。
关键点:一阶段就提交本地事务,性能远优于传统 XA;undo_log 是后续回滚的补偿依据。
AT 模式 第二阶段:两种分支走向(提交 / 回滚)
场景 1:全局正常提交(所有分支一阶段都成功)
- TM 判断全部分支执行无误,向 TC 发起全局提交指令。
- TC 通知所有参与该 XID 的 RM 执行分支提交。
- RM 收到提交指令:直接删除自身对应的 undo_log 记录,不需要修改业务数据(一阶段数据已经落库)。
- RM 上报分支提交完成,TC 标记全局事务完结,释放全局锁。
为什么不用改业务数据?一阶段本地事务已经把数据改完了,提交只是清理回滚日志。
场景 2:全局回滚(任意分支异常、超时、抛出异常)
- TM 捕获异常,向 TC 发起全局回滚指令。
- TC 通知所有 RM 执行分支回滚。
- RM 查询当前 XID 对应的 undo_log,对比当前数据库最新数据与 after_image:
- 数据未被其他事务修改:用 before_image 反向执行补偿 SQL,恢复原始数据
- 数据已被修改(脏写冲突):触发锁等待 / 回滚报错,保证数据一致性
- 补偿 SQL 执行完成,删除 undo_log,提交本地补偿事务。
- RM 上报回滚完成,TC 标记全局事务回滚结束,释放全局锁。
AT 两大核心机制(解决数据一致性 + 并发脏写)
-
Undo Log 回滚日志
补偿回滚基石,一阶段同步写入,二阶段提交删除、回滚执行逆 SQL。
-
全局锁(Global Lock)
解决并发修改同一行数据脏写问题:一阶段修改数据后占用全局锁,其他事务修改该行会被阻塞,直到当前全局事务二阶段完成释放锁。
三、TM、TC、RM 生动生活化案例(下单扣库存扣余额)
业务场景
用户下单:
服务 A(订单服务 TM)→ 创建订单
服务 B(库存服务 RM1)→ 扣商品库存
服务 C(账户服务 RM2)→ 扣用户余额
三个服务三个数据库,必须要么全部成功,要么全部回滚,用 Seata AT 保证分布式一致。
TC = 小区物业总管;TM = 发起办事的业主;RM = 两家门店
角色对应
- TC(物业总管):登记整件事台账,统筹决定整件事办成功还是作废,通知两家门店执行收尾
- TM(下单服务 / 业主):发起 "下单扣款减库存" 整件事,事情出问题就通知总管全部撤销
- RM1(库存门店)、RM2(账户门店):各自处理自己业务,听总管指令收尾
完整分步协作流程
一阶段(分头办事,各自留反悔凭证)
- TM(下单服务)找到 TC:帮我开一个全局办事单号 XID,我要发起一笔分布式下单事务。TC 登记全局事务,生成 XID。
- TM 调用库存服务 RM1:
- RM1 查库存原有数量(before 镜像),扣减库存(after 镜像)
- 在自己库存库同一个本地事务:改库存 + 写 undo_log(反悔凭证)
- 向 TC 登记:我是分支 1,XID 这件事我一阶段办完了
- 提交库存本地事务,库存数据真实变少,本地锁释放
- TM 继续调用账户服务 RM2:
- RM2 查用户原有余额(before 镜像),扣减余额(after 镜像)
- 账户库同一个本地事务:扣余额 + 写 undo_log 反悔凭证
- 向 TC 登记:我是分支 2,XID 这件事我一阶段办完了
- 提交账户本地事务,余额真实变少
- TM 汇总:两个分支全部一阶段执行成功,等待最终决策。
二阶段情况 1:全部正常 → 全局提交
-
TM 告诉 TC:整件事没问题,全局提交。
-
TC 下发指令给 RM1、RM2:你们各自收尾。
-
RM1 删掉自己库存库 undo_log(反悔凭证作废);RM2 删掉账户库 undo_log。
-
TC 标记整个全局事务完成,释放全局锁。
最终结果:订单创建、库存扣减、余额扣减,全部生效,数据一致。
二阶段情况 2:扣余额时报异常(余额不足抛异常)→ 全局回滚
-
RM2 执行报错,异常向上抛到 TM。
-
TM 立刻通知 TC:整件事作废,全局回滚。
-
TC 给 RM1、RM2 下发回滚指令。
-
RM2 通过 undo_log 里修改前余额数据,执行补偿 SQL,把扣掉的余额加回去,删除 undo_log。
-
RM1 通过 undo_log 原来库存数据,把扣掉的库存加回去,删除 undo_log。
-
TC 标记全局事务回滚结束,释放全局锁。
最终结果:订单创建回滚删除、库存不变、余额不变,三方数据全部还原,满足最终一致性。
四、Seata 其他三种模式简要对比(XA / TCC / SAGA)
1. XA 模式(传统数据库两阶段,强一致性)
- 原理:数据库原生支持 XA 协议,RM 是数据库 XA 驱动,一阶段锁定资源预提交,二阶段统一提交 / 回滚
- 优点:无业务侵入,强一致性
- 缺点:一阶段长时间占用行锁,并发性能极差,MySQL 等数据库兼容性一般
- 适用:低并发、对一致性要求极高的传统内部系统
2. TCC 模式(侵入式手动补偿,高性能)
TCC = Try-Confirm-Cancel,业务代码手写三阶段逻辑
- Try:预留资源(冻结库存、冻结余额)
- Confirm:确认扣减,正式提交
- Cancel:释放预留资源,回滚补偿
- 优点:不依赖 undo_log,无数据库锁阻塞,性能最好,自由度最高
- 缺点:侵入极强,每个接口都要手写三套逻辑,开发维护成本极高
- 适用:高并发核心交易、金融支付场景
3. SAGA 模式(长事务、柔性事务,反向补偿)
- 正向:每个业务步骤正常执行;任一失败,反向执行前面所有步骤的补偿业务接口
- 无锁设计,无预留阶段,适合长流程分布式事务(订单履约、物流、审批长链路)
- 优点:适合超长事务、跨异构系统(非数据库)
- 缺点:仅保证最终一致性,存在中间数据脏数据,无隔离性,补偿逻辑容易写乱
四种模式极简总结表格
| 模式 | 侵入性 | 一致性 | 性能 | 核心原理 |
|---|---|---|---|---|
| AT | 极低(注解) | 最终一致 | 优 | undo_log 自动补偿 + 全局锁 |
| XA | 无侵入 | 强一致 | 差 | 数据库原生 XA 两阶段锁 |
| TCC | 极高(手写三接口) | 最终一致 | 最优 | 手动预留 + 确认 / 撤销 |
| SAGA | 中(写补偿接口) | 最终一致 | 良好 | 失败逆向补偿 |