seata at模式回滚依赖undo_log表、数据源代理和两阶段补偿机制,通过快照比对与反向sql实现;需建undo_log表、用datasourceproxy包装数据源、正确配置@globaltransactional,并避免脏写与不支持场景。

Seata 的 AT(Automatic Transaction)模式回滚是通过全局事务协调器(TC)、事务参与者(RM)和事务发起方(TM)协同完成的,核心在于自动代理数据源 + 两阶段提交 + 补偿日志(undo_log)。回滚不是靠数据库事务回滚,而是靠 Seata 在业务 SQL 执行前记录快照、执行后生成反向 SQL,失败时用 undo_log 中的快照比对并执行补偿逻辑。
一、AT 模式回滚的前提:undo_log 表必须存在
每个参与分布式事务的数据库,都必须建一张 undo_log 表(Seata 官方提供 DDL),用于存储每条业务 SQL 执行前后的镜像(beforeImage / afterImage)和回滚语句。没有这张表,RM 无法生成 undo 日志,回滚直接失败。
- MySQL 示例建表语句(注意字段名、索引、引擎必须严格一致):
CREATE TABLE IF NOT EXISTS `undo_log` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `branch_id` BIGINT(20) NOT NULL, `xid` VARCHAR(100) NOT NULL, `context` VARCHAR(128) NOT NULL, `rollback_info` LONGBLOB NOT NULL, `log_status` INT(11) NOT NULL, `log_created` DATETIME NOT NULL, `log_modified` DATETIME NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4;
二、回滚触发时机与流程
当 TM 调用 globalTransaction.rollback()(如全局异常未被捕获、显式调用 rollback),或 TC 检测到分支注册超时/响应失败时,TC 向所有已注册的 RM 发起 第二阶段回滚指令。RM 收到后,按以下步骤执行本地回滚:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 根据
xid和branch_id查询 undo_log 表中对应记录 - 解析
rollback_info得到 beforeImage(修改前快照)和 afterImage(修改后快照) - 查询当前数据库中该行最新数据,与 afterImage 做校验(防止脏写,若不一致则回滚失败,进入“悬挂”状态)
- 校验通过后,基于 beforeImage 生成反向 SQL(如 UPDATE → SET 字段 = old_value),执行更新还原
- 成功后删除该 undo_log 记录
三、关键配置与注意事项
AT 回滚能否成功,高度依赖框架集成和参数配置:
-
数据源需被 Seata DataSourceProxy 包装:Spring Boot 中通过
@Bean返回DataSourceProxy(new HikariDataSource()),否则 SQL 不会被拦截,无法记录 undo_log -
全局事务注解 @GlobalTransactional 必须加在入口方法上(如 Controller 或 Service 外层),且异常不能被吞掉;默认只对 RuntimeException 回滚,如需检查异常也触发,加
rollbackFor = Exception.class - 避免在 AT 分支中手动使用 Connection.commit()/rollback(),会破坏 Seata 的事务上下文管理
- UPDATE/DELETE 必须带 WHERE 条件,且条件字段要有索引,否则 beforeImage/afterImage 可能不准确,导致校验失败而回滚中断
四、常见回滚失败原因与排查方向
回滚失败通常不会抛出明显异常,但日志里有线索:
- undo_log 表无记录 → 检查数据源是否被 Proxy 包装、SQL 是否被识别(开启 seata.client.log.level=debug 可看代理日志)
- 回滚日志显示 “Data has changed” 或 “Rollback failed” → beforeImage 校验失败,说明该行被其他事务/线程抢先修改,属典型脏写,需加业务幂等或乐观锁
- TC 日志报 “branch rollback failed” + RM 端无 undo_log 查询日志 → RM 未收到回滚请求,检查 TC 与 RM 网络连通性、application.yml 中 registry 和 config 配置是否指向同一配置中心
- 回滚后数据未恢复,但 undo_log 被删了 → 补偿 SQL 执行成功但效果不符预期,检查 SQL 是否涉及函数(如 NOW()、UUID())、自增主键、触发器等 Seata 不支持场景
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










