最近有同事问:“Seata 是怎么做到分布式事务回滚的?它又不是数据库,凭什么把已经写进去的数据删掉?”
这个问题问得好。很多人用 Seata,知道加个 @GlobalTransactional 就能跨服务回滚,但对背后原理一知半解。今天我们就拿一条真实的 undo_log 记录,拆开看看 Seata 到底是怎么实现数据回退的。
一、先说结论:Seata 不是靠数据库回滚,而是自己执行“反向操作”
传统本地事务靠数据库的 redo/undo 日志实现回滚。但在分布式场景下,每个服务连接自己的数据库,Seata 作为协调者,并不能直接命令 MySQL “把刚才那条 INSERT 撤销”。
它的做法更直接:在你执行 SQL 的同时,同步记录下“如果要回滚,该怎么把它改回去”。这个“回滚指令”,就存在每个业务库里的 undo_log 表中。
换句话说:Seata 的回滚,是应用层主动执行的一条“反向 SQL”。
二、看一条真实的 undo_log 记录
这段 JSON,就是 Seata 存在 undo_log 表里的一条记录(已格式化):
{
"xid": "121.41.42.84:8091:427886399291682816",
"branchId": 427886417402687489,
"sqlUndoLogs": [
{
"sqlType": "INSERT",
"tableName": "tb_points_mall_order_detail",
"beforeImage": { /* 空 */ },
"afterImage": {
"rows": [
{
"fields": [
{"name":"id", "value":186},
{"name":"order_id", "value":8},
{"name":"goods_name", "value":"舒缓新肌焕颜乳"},
// ... 其他字段
]
}
]
}
}
]
}
重点看这几个部分:
sqlType: "INSERT":说明这条 SQL 是插入操作。beforeImage是空的:因为插入前,这条记录不存在。afterImage保存了刚插入的整行数据。
当全局事务需要回滚时,Seata 的 RM(Resource Manager)模块会读取这条记录,发现是 INSERT,于是生成一条对应的 DELETE 语句:
DELETE FROM tb_points_mall_order_detail WHERE id = 186;
然后执行它——数据就被撤销了。
三、不同 SQL 类型,回滚方式不同
Seata 对三种基本操作分别处理:
| 操作类型 | beforeImage | afterImage | 回滚动作 |
|---|---|---|---|
| INSERT | 空 | 插入后的完整行 | DELETE 主键匹配的记录 |
| UPDATE | 修改前的旧值 | 修改后的新值 | UPDATE 把字段改回旧值 |
| DELETE | 删除前的完整行 | 空 | INSERT 把整行重新插回去 |
举个 UPDATE 的例子:
UPDATE account SET balance = 90 WHERE user_id = 1001;
Seata 会先查出 balance = 100(旧值),存到 beforeImage;执行完 UPDATE 后,把 balance = 90 存到 afterImage。
回滚时,就执行:
UPDATE account SET balance = 100 WHERE user_id = 1001;
这其实就是一种自动化的补偿逻辑,也是 Seata AT 模式的核心机制。
四、为什么要有主键?
注意上面所有回滚 SQL 都依赖主键定位记录。这也是为什么 Seata 要求表必须有主键——没有主键,就无法准确识别要回滚的具体行。
如果你的表没主键,Seata 会在事务开始阶段直接报错,不会继续执行。
五、undo_log 表什么时候清理?
- 事务提交成功:Seata 会在异步阶段自动删除对应的
undo_log记录(默认延迟 1 秒)。 - 事务回滚:回滚完成后立即删除。
- 异常残留:极少数情况下(如服务宕机),可能留下脏数据。Seata 提供了后台清理机制,也可以手动处理。
因此,这张表在正常运行时不会持续增长,无需过度担心存储问题。
六、和 Atomikos 有啥区别?
有人会问:之前用 Atomikos 也能回滚,Seata 有什么不同?
- Atomikos:基于数据库的 XA 协议,由数据库自身完成两阶段提交和回滚。
- Seata(AT 模式) :不依赖数据库 XA 支持,而是在应用层拦截 SQL、生成 undo log,通过补偿方式实现回滚。对数据库要求更低(只需有主键),性能也更优。
简单说:Atomikos 依靠数据库的能力完成回滚,Seata 则通过应用层构造反向 SQL 实现回滚。
七、总结
Seata 的回滚能力,源于一套清晰的“快照 + 补偿”机制:
- 执行业务 SQL 前,查询并保存“前镜像”;
- 执行后,保存“后镜像”;
- 将两者打包写入
undo_log; - 回滚时,根据操作类型生成并执行反向 SQL。
整个过程对业务代码透明,开发者只需关注业务逻辑,Seata 在底层完成数据一致性保障。
理解了这一点,再看 undo_log 表,就不会觉得神秘——它本质上是一份结构化的变更日志,明确记录了如何撤销一次本地修改。
遇到回滚异常时,查看 undo_log 表中的内容,往往能快速定位问题根源。