Seata 的 AT 模式原理是什么?为什么能自动回滚?
简化版
Seata AT 模式是对业务无侵入的自动补偿型分布式事务。它靠代理数据源,在你执行 SQL 时自动记录数据修改前后的镜像(undo log),一阶段就直接提交本地事务(释放数据库锁),二阶段若全局事务成功则异步删掉 undo log,失败则用 undo log 里的「前镜像」自动生成反向 SQL 把数据改回去。它相当于「自动版的 TCC/Saga」,你只管写业务 SQL 加个 @GlobalTransactional 注解,回滚由框架自动做。
详细版
三个角色:
- TC(Transaction Coordinator,事务协调器):独立部署的 Seata Server,维护全局事务状态、协调提交/回滚。
- TM(Transaction Manager,事务管理器):发起全局事务的一方(加了
@GlobalTransactional的方法),决定全局提交还是回滚。 - RM(Resource Manager,资源管理器):管理各分支事务(每个参与的数据库),负责注册分支、上报状态、执行提交/回滚。
两阶段流程:
- 一阶段:RM 拦截业务 SQL,解析它 → 查询前镜像(改之前的数据)→ 执行业务 SQL → 查询后镜像(改之后的数据)→ 把前后镜像存进
undo_log表 → 提交本地事务(业务数据和 undo log 一起提交,本地锁马上释放)→ 向 TC 注册分支并申请全局锁。 - 二阶段:
- 全局提交:TC 通知各 RM,异步删除 undo log 即可(数据已经在一阶段提交了)。
- 全局回滚:TC 通知各 RM,RM 用 undo log 的前镜像生成反向 SQL,把数据恢复原样,再删 undo log。
关键机制:
- undo log:记录数据变更前后的快照,是自动回滚的依据。
- 全局锁:Seata 自己维护的行级锁,防止两个全局事务同时改同一行导致回滚时数据错乱(保证写隔离)。
完整版教学
一、AT 模式解决什么:既要自动、又要高性能
2PC/XA 对业务无侵入但锁数据库太久(性能差);TCC 性能好但要写三个方法(侵入大)。AT 模式想两全:像 XA 一样对业务透明(只加注解、写普通 SQL),又像 TCC 一样不长时间锁数据库。它的做法是——一阶段直接提交本地事务(马上释放 DB 锁,这是性能关键),把「怎么回滚」的信息(undo log)提前记下来,需要回滚时再用它反向操作。
二、undo log:自动回滚的核心
AT 最巧妙的地方是自动生成回滚。它通过数据源代理拦截你的 SQL,在执行前后各查一次数据:
- 前镜像(before image):SQL 执行前,这些行长什么样。
- 后镜像(after image):SQL 执行后,这些行长什么样。
这两份快照连同 SQL 信息存进 undo_log 表,和业务数据在同一个本地事务里提交。回滚时,框架拿前镜像生成一条把数据改回去的反向 SQL(比如 update 后镜像 → 前镜像)。所以你不用写任何补偿代码,框架靠镜像自动”倒带”。删 undo log 也是在同事务或异步进行。
记忆点:AT = 一阶段直接提交 + 存前后镜像;回滚时拿前镜像自动生成反向 SQL 倒带。「自动补偿」的自动,就来自 undo log 镜像。
三、全局锁:解决 AT 的隔离性问题
因为一阶段就提交了本地事务、释放了数据库锁,如果不加控制,另一个全局事务可能在这期间也改了同一行并提交,那么第一个事务回滚时用「前镜像」倒带就会覆盖掉第二个事务的修改(脏写、更新丢失)。Seata 用全局锁解决:一阶段提交前,RM 要向 TC 申请这些行的全局锁,拿到才提交;别的全局事务想改同一行必须先拿全局锁,拿不到就等待/回滚。这保证了全局事务之间对同一行的写隔离。
注意:全局锁只锁「参与全局事务的写」。默认隔离级别是读未提交(别的普通查询能读到中间态),如需读隔离要用 SELECT ... FOR UPDATE(会走全局锁校验)。
四、脏写与「回滚失败」的兜底
极端情况:如果有人绕过 Seata 直接改了数据(没走全局锁),回滚时框架会对比「当前数据」和「后镜像」——如果不一致,说明数据被别人动过,此时无法安全倒带,Seata 会报错并挂起,转人工处理,避免错误覆盖。这也是为什么用 AT 模式时,所有对相关表的写都应经过 Seata。
五、AT vs TCC vs XA(Seata 四模式里的三个)
| 维度 | XA 模式 | AT 模式 | TCC 模式 |
|---|---|---|---|
| 侵入 | 无 | 无(加注解) | 大(三个方法) |
| 锁 | DB 锁,全程 | 一阶段就释放 DB 锁,用全局锁 | 业务预留 |
| 回滚 | DB 原生 | undo log 自动反向 | 手写 Cancel |
| 依赖 | DB 支持 XA | 需 undo_log 表 | 纯业务 |
| 性能 | 差 | 较好 | 好 |
| 适用 | 强一致低并发 | 通用、无侵入首选 | 资金/资源强约束 |
AT 是 Seata 最常用的默认模式,兼顾无侵入和性能,适合大多数业务;对隔离性要求极高或资金核心用 TCC。
六、常见误区与追问
这道题不能只背概念,要把「Seata AT 模式」放回真实分布式系统里解释:谁发起、谁协调、状态如何变化、失败后怎么兜底,以及它和性能、可用性、一致性的取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | Seata AT 通过全局锁和 undo_log 在本地事务基础上自动生成回滚日志,实现非侵入式最终一致 | 不要停在名词解释 |
| 流程机制 | TC 开启全局事务 -> RM 执行本地 SQL 并记录 undo_log -> 提交本地事务并释放本地锁 -> 全局提交删除 undo_log -> 全局回滚用 undo_log 补偿 | 说明触发方、参与方、状态变化和兜底 |
| 工程取舍 | 更新库存前记录 before image 和 after image,异常时用 undo_log 反向恢复 | 分布式事务没有免费强一致,性能、可用性和业务侵入一定要取舍 |
Seata AT 模式 面试拆解:
1. TC 开启全局事务
2. RM 执行本地 SQL 并记录 undo_log
3. 提交本地事务并释放本地锁
4. 全局提交删除 undo_log
5. 全局回滚用 undo_log 补偿
记忆钩子:先说本地事务失效的原因,再拆协调、补偿、消息、幂等、对账和回滚边界;回答时要紧扣「Seata AT 模式」这道题,不要把相邻概念混成一段泛泛的分布式套话。
- 误区:AT 模式等于 XA 强一致。 AT 一阶段本地提交,二阶段靠 undo_log 补偿,属于最终一致思路。
- 误区:AT 对业务完全无要求。 需要表有主键、SQL 可解析,并注意脏写和全局锁冲突。
- 误区:undo_log 可以随便删。 未完成全局事务依赖 undo_log 回滚,清理必须按状态安全执行。
- 追问:全局锁解决什么? 防止其他事务修改已被全局事务占用的数据,避免回滚时数据被污染。
- 追问:AT 优势是什么? 业务侵入低,适合关系型数据库常规 SQL 场景。
- 追问:AT 不适合什么? 复杂 SQL、非关系型资源、长事务和高冲突热点更新。
七、加强记忆
Seata AT 模式 = 无侵入自动补偿事务,靠 TC/TM/RM 三角色协作。一阶段拦截 SQL、存前后镜像到 undo_log、直接提交本地事务并释放 DB 锁、注册分支申请全局锁;二阶段全局提交就异步删 undo log,全局回滚就用 undo log 的前镜像自动生成反向 SQL 倒带。核心是 undo log 实现自动回滚、全局锁保证全局事务间写隔离。你只需 @GlobalTransactional + 普通 SQL,兼顾无侵入与高性能,是 Seata 默认首选模式。