目录
一、前置基础
1. AT 定位
2. 三大核心角色(和标准 2PC 一致)
3. 必须依赖的业务表(每个业务库都要建)
4. 两个核心机制
二、AT 完整两阶段流程(核心)
阶段一:一阶段提交(本地事务直接提交,核心优化点)
阶段二:二阶段两种分支
分支 1:全局提交(所有分支一阶段成功)
分支 2:全局回滚(任意分支一阶段失败 / 超时)
三、全局锁与隔离性(解决脏写)
1. 场景说明
2. 隔离级别
四、AT 对比 XA 2PC(核心差异)
五、AT 优缺点
优点
缺点
六、典型业务流程示例(下单 + 扣库存)
七、关键补充:幂等与防悬挂
八、适用场景
一、前置基础
1. AT 定位
AT = Automatic Transaction,自动事务模式,是 Seata 最主流、无代码侵入的分布式事务方案,底层是改良版二阶段提交 2PC,规避了原生 XA 2PC 长锁、性能差的问题。
- 原生 XA:数据库层面实现 2PC,Prepare 阶段占用行锁直到全局提交 / 回滚,阻塞严重;
- Seata AT:业务 SQL 执行直接提交本地事务,用快照 + 全局锁实现隔离,大幅降低锁持有时间。
2. 三大核心角色(和标准 2PC 一致)
- TC (Transaction Coordinator):事务协调器,独立服务(seata-server),记录全局事务、分支事务状态,下发提交 / 回滚指令。
- TM (Transaction Manager):事务发起方,业务服务,通过
@GlobalTransactional开启全局事务。 - RM (Resource Manager):资源管理器,每个微服务的数据库代理,拦截 SQL、生成回滚日志、上报 TC、执行分支提交 / 回滚。
3. 必须依赖的业务表(每个业务库都要建)
sql
CREATE TABLE `undo_log` ( `id` bigint AUTO_INCREMENT, `branch_id` bigint NOT NULL COMMENT '分支事务ID', `xid` varchar(128) NOT NULL COMMENT '全局事务ID', `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL COMMENT '数据快照,用于回滚', `log_status` int NOT NULL COMMENT '0正常 1已提交 2已删除', `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB;undo_log:分支事务的回滚快照表,AT 模式核心。
4. 两个核心机制
- Undo Log 回滚快照:更新 / 删除前记录修改前数据,失败时恢复;
- 全局锁(Global Lock):解决分布式脏写,保证隔离性。
二、AT 完整两阶段流程(核心)
AT 同样分一阶段 Prepare、二阶段 Commit/Rollback,但和 XA 2PC 逻辑完全不同。 全局唯一标识:XID,TM 生成,所有微服务透传。
阶段一:一阶段提交(本地事务直接提交,核心优化点)
流程:TM 开启全局事务 → 各个 RM 执行业务 SQL,同步完成三件事:
前置镜像查询(Before Image)执行 Update/Delete 前,查询当前行所有字段,存入 undo_log 的 rollback_info。 例:
update stock set num = num -1 where id = 1先查 id=1 的原始库存num=100,保存快照。执行业务 SQL,提交本地事务直接执行更新,本地数据库事务立刻 commit,释放本地行锁! 这是和 XA 最大区别:XA Prepare 不提交、锁长期持有;AT 一阶段直接提交本地事务,本地锁马上释放,并发性能大幅提升。
后置镜像(After Image)+ 插入 undo_logSQL 执行后查询修改后数据,一并存入 undo_log,标记日志状态
log_status=0(未处理)。向 TC 注册分支、申请全局锁RM 上报 TC:当前分支事务修改了哪些主键行,抢占全局锁。
- 全局锁作用:其他分布式事务不能修改同一行,防止脏写,实现读已提交隔离;
- 全局锁存储在 TC 内存,不是数据库锁,本地锁已经释放。
一阶段收尾 所有分支全部完成一阶段提交,TC 标记全局事务状态为
committing,进入二阶段。
一阶段总结:本地事务已经落库,只是留存 undo_log,数据处于 “临时有效” 状态,依赖全局锁隔离其他事务。
阶段二:二阶段两种分支
分支 1:全局提交(所有分支一阶段成功)
TC 下发 commit 指令给所有 RM:
- RM 收到提交指令;
- 删除当前分支对应的 undo_log 记录(标记 log_status=1 或直接删除);
- 释放 TC 上持有的全局锁;
- 分支处理完成,通知 TC;
- TC 收到全部分支 ack,全局事务结束。
关键点:提交时不用改业务数据,只删回滚日志,极快,无数据库 IO 压力。
分支 2:全局回滚(任意分支一阶段失败 / 超时)
TC 下发 rollback 指令给 RM:
- RM 查询当前分支的 undo_log,取出 Before Image 修改前快照;
- 根据前置镜像反向补偿执行 SQL,恢复数据到修改前状态; 例:之前扣减库存 num-1,回滚执行
update stock set num=100 where id=1; - 补偿 SQL 执行完成,本地事务提交;
- 删除 undo_log,释放全局锁;
- 回复 TC,全局事务终止。
故障兜底:如果回滚时 RM 宕机,重启后 RM 会主动向 TC 查询 XID 状态,自动重试回滚,保证幂等。
三、全局锁与隔离性(解决脏写)
1. 场景说明
事务 A(全局事务)一阶段更新库存 id=1,本地锁释放,但持有 TC 全局锁; 事务 B(另一个全局事务)也要更新 id=1:
- B 一阶段执行 SQL 更新成功,但去 TC 申请全局锁时发现已被 A 占用;
- B 分支一阶段阻塞,等待全局锁;
- 若 A 最终回滚 / 提交释放全局锁,B 才能拿到锁完成注册;
- 若等待超时,B 自动回滚本地修改。
2. 隔离级别
AT 默认读已提交,不支持可重复读:
- 普通本地读:可以读到其他全局事务一阶段提交的未确认数据;
- 写操作受全局锁控制,不会出现脏写; 如果需要可重复读,使用 Seata 新的 AT 快照隔离模式或 TCC。
四、AT 对比 XA 2PC(核心差异)
| 对比项 | XA 2PC | Seata AT |
|---|---|---|
| 一阶段行为 | 执行 SQL,不提交本地事务,持有行锁 | 执行 SQL,直接提交本地事务,释放行锁 |
| 锁资源 | 数据库行锁长期阻塞,并发差 | 本地锁快速释放,仅 TC 全局锁短持有 |
| 回滚方式 | 数据库原生 undo 回滚 | 业务前置镜像反向补偿更新 |
| 二阶段开销 | 提交 / 回滚都操作业务数据 | 提交仅删 undo_log,轻量 |
| 代码侵入 | 无,数据库原生支持 | 无,仅加注解@GlobalTransactional |
| 性能 | 差,高并发锁等待严重 | 优秀,互联网主流 |
| 依赖 | 数据库支持 XA | 业务库必须创建 undo_log 表 |
五、AT 优缺点
优点
- 零业务代码侵入:只需要注解开启全局事务,不用手写补偿逻辑;
- 性能远优于 XA,本地锁快速释放,适合高并发微服务;
- 回滚自动兜底,故障重启自动恢复,保证最终一致性;
- 兼容所有支持 JDBC 的数据库(MySQL、Oracle、PostgreSQL 等)。
缺点
- 需要额外维护 undo_log 表,每次写操作多两次镜像查询(前置 + 后置),增加少量 SQL 开销;
- 仅保证读已提交隔离,存在不可重复读;
- 只支持 DML(Insert/Update/Delete),DDL 语句不支持;
- 长事务场景会长期占用 TC 全局锁,影响并发;
- 极端网络分区下,会短暂出现中间状态数据,属于最终一致性,非实时强一致。
六、典型业务流程示例(下单 + 扣库存)
服务 A:订单服务(TM,开启全局事务) 服务 B:库存服务(RM)
- TM 生成 XID,调用库存服务,XID 通过请求头传递;
- 库存 RM 一阶段: 查原库存快照 → 扣减库存、本地 commit → 写入 undo_log → 向 TC 申请全局锁;
- 订单 RM 一阶段: 插入订单快照 → 新增订单、本地 commit → 写入 undo_log → 注册分支;
- TC 收到两个分支全部成功,下发二阶段 commit;
- 订单、库存 RM 删除各自 undo_log,释放全局锁;
- 全局事务完成,数据永久生效。
异常场景:库存服务一阶段成功,订单服务抛异常: TC 收到失败信号,向库存 RM 下发 rollback; 库存 RM 读取 undo_log 前置镜像,恢复原始库存,删除日志。
七、关键补充:幂等与防悬挂
- 防悬挂二阶段回滚请求先到达 RM,一阶段 SQL 后到,会出现空回滚。 解决:回滚时先插入一条空 undo_log 占位,后续一阶段执行时发现已有 undo 记录,直接放弃执行业务 SQL,避免悬挂脏数据。
- 幂等保证TC 会重复下发 commit/rollback;RM 通过 xid+branch_id 唯一索引判断 undo_log 是否已处理,重复指令直接忽略,不会重复回滚 / 删除。
八、适用场景
- 绝大多数电商、支付、供应链微服务,常规短事务;
- 追求开发效率(无侵入)、并发性能; 不适合:超长事务、DDL 操作、要求可重复读隔离级别的场景。