Seata 的事务回滚是怎么实现的?
- 标签:Spring Cloud 、微服务、后端
🏛 业务架构师
Seata 的回滚实现按模式分述,核心是"TC 协调 + 分支补偿":
流程骨架:TM 发起全局事务 → 各 RM 执行分支并注册到 TC → 任何分支抛异常时 TM 向 TC 发起全局回滚 → TC 逐个通知分支 RM 回滚 → 全部成功后全局事务标记 Rollbacked。
AT 模式的回滚(重点):
- RM 收到回滚指令,找到该分支的 undo_log(记录着修改前镜像 before image / 修改后镜像 after image);
- 数据校验:比对当前数据与 after image 是否一致——防止提交后到回滚前数据被别的本地事务改过;
- 校验通过 → 按 before image 反向生成补偿 UPDATE SQL,恢复数据,删 undo_log,释放全局锁;
- 校验失败(脏写)→ 转人工处理,记录异常——这正是"为什么 AT 之后还要对账兜底"的原因。
TCC 模式的回滚:TC 依次调用各分支的 Cancel 方法(业务自己写的释放逻辑),框架层面靠防悬挂 fence 表 + 空回滚检查 + 幂等控制保证 Cancel 的正确性。
Saga 模式:反向执行补偿服务链(逆序补偿),补偿失败重试直至人工介入。
架构师视角的业务要点:
- 回滚能力依赖分支执行的时序记录,所以分支事务状态、undo_log 都要有监控与巡检——悬挂全局事务(既没提交也没回滚)要能自动发现;
- 回滚不是免费的:补偿逻辑的边界情况(网络分区下指令重试)决定了方案的可靠性下限,上线前故障演练(杀 TC、断网)验证回滚路径是我带的团队的必做项。
🏗 系统架构师
回滚执行链:TM 决策回滚 → TC 逐分支下发 undo → RM 按序执行校验与补偿:
- 取出 undo_log:找到该分支的 beforeImage(改前镜像)与 afterImage(改后镜像);
- 数据校验(关键防线):比较 afterImage 与库里当前实际数据——一致说明无人改动,可安全回滚;不一致说明脏写(事务外有人改了这行),不能盲目覆盖,进入重试并告警人工介入;
- 反向补偿:按 beforeImage 生成反向 SQL(update 回旧值,insert 反向是 delete),在本地事务里执行后删除 undo_log;
- 校验失败的分支按策略重试,超限挂起等待人工处理(Seata 控制台可查全局事务状态)。
架构要点:
- 反向补偿成立的前提是业务纪律:同一行数据不得在全局事务之外被修改——定时任务、DBA 手工 DML、未接 Seata 的服务都是脏写源,这是 AT 最常见的事故根因;
- 补偿必须幂等(网络重试会重复下发);
- 监控指标:undo 挂起数、回滚重试数——异常即数据一致性风险预警,我的运维大盘必放。
💻 全栈开发专家
回滚链路五步:
- 触发:TM 侧异常或超时,向 TC 发起全局回滚
- 分支定位:TC 按 XID 找出所有注册分支,下发回滚指令到各 RM
- 取 undo_log:RM 根据 XID + branchId 在业务库取前后镜像
- 数据校验(防脏写):当前记录与后镜像比对——一致说明一阶段后没人改过,可安全补偿;不一致(被别的全局事务或裸 SQL 改过)则记日志转人工处理,这是 AT 一致性的最后防线
- 反向 SQL 补偿:按前镜像生成逆操作——insert 反演 delete(按主键)、delete 反演 insert(回插全字段)、update 反演 update 回旧值;反向 SQL 与删除 undo_log 在同一本地事务提交,之后释放全局锁
全局锁机制保证回滚期间没有其他全局事务能改这行。
排障经验,回滚失败常见三类:undo_log 表没建(RM 部署遗漏)或被业务代码误清;后镜像校验不匹配(存在绕过 Seata 代理直接改数据的裸 SQL);字段类型/blob 配置不匹配。盯 TC 的 global_table/branch_table 和业务库 undo_log 就能还原现场。