seata 是一个独立的分布式事务中间件,非 mysql 自带功能,用于解决跨库、跨服务、跨语言场景下单机事务无法保证一致性的根本问题;它通过全局事务id分发、分支事务注册、undo_log快照记录与补偿回滚,实现高性能、无侵入的最终一致性,绕过mysql原生xa的可靠性差、无自动清理、无持久化协调等缺陷。

Seata 不是 MySQL 自带的能力,它是一个独立的分布式事务中间件,用来解决 MySQL 跨库(甚至跨服务、跨语言)场景下无法靠单机事务保证一致性的根本问题。
MySQL 本身只支持单实例内跨库(同一 MySQL 实例下的多个 database)的 XA 事务;一旦涉及多个 MySQL 实例(比如 db-a.example.com 和 db-b.example.com),原生 XA 就需要外部事务管理器(TM)协调,而 MySQL 自身不提供高可用、可运维的 TM。这时候 Seata 才真正派上用场。
为什么不能直接在应用里手写 XA 提交逻辑?
你完全可以用 PyMySQL 或 JDBC 手动调用 XA START / XA PREPARE / XA COMMIT,但会立刻撞上这几个硬伤:
-
XA RECOVER结果不可靠:MySQL 的XA RECOVER只返回当前节点上未完成的 XA 事务 ID,不包含上下文(比如业务单号、分支信息),崩溃后无法安全判断该 commit 还是 rollback - 没有超时自动清理:一个卡在
PREPARED状态的 XA 事务会一直持有行锁和连接,直到 DBA 手动XA ROLLBACK,线上不敢这么玩 - 跨实例无全局事务 ID 分发:应用得自己生成唯一
xid并确保所有参与库都收到——这已经是在重复实现 TM 的基础功能 - 没有日志持久化与重试:网络抖动导致
XA COMMIT指令丢失?MySQL 不会帮你重发,应用层也很难精准判断是否已提交
Seata AT 模式如何绕过 XA 的缺陷?
Seata 的 AT(Automatic Transaction)模式不依赖 MySQL 的 XA,而是用“全局事务 + 分支事务 + 回滚快照”三段式来模拟两阶段提交,核心在于:
- 应用开启全局事务时,
Seata生成全局xid,透传到所有微服务调用链路 - 每个本地 SQL 执行前,
Seata的 JDBC 代理会自动查出变更前后的数据镜像(before_image/after_image),存入undo_log表 - 全局提交时,只删掉
undo_log记录;全局回滚时,用before_image逆向生成补偿 SQL 并执行 - 所有协调状态(如分支注册、状态上报)由
Seata Server持久化存储,崩溃后可恢复
这意味着你写的是普通 UPDATE,不是 XA PREPARE;MySQL 不用开 XA 支持;也不用担心锁住半天没人管。
实际接入 Seata 的关键配置点
不是加个依赖就能跑通,这几个地方配错,事务就形同虚设:
- MySQL 必须为每个参与库单独建
undo_log表(Seata不自动创建),表结构必须严格匹配官方 DDL,字段名大小写、索引顺序都不能错 -
application.yml中的service.vgroup-mapping必须和业务代码中@GlobalTransactional的value一致,否则Seata Server根本收不到分支注册 - 数据源必须被
SeataDataSourceProxy包装,直连原生DataSource会导致undo_log写不进去,且无任何报错 -
Seata Server的store.mode推荐用db(非file),否则集群重启后事务状态丢失
什么时候该放弃 Seata,换别的方案?
Seata AT 对 MySQL 的 DML 支持好,但对 DDL、存储过程、SELECT FOR UPDATE 外的锁机制、或涉及 INSERT ... ON DUPLICATE KEY UPDATE 这类“读写混合”语句,undo_log 可能无法正确生成补偿逻辑。此时应考虑:
- 改用
Seata TCC模式:自己写try/confirm/cancel方法,控制力最强,但开发量翻倍 - 退回到基于消息队列的最终一致性:用
RocketMQ事务消息 + 本地事务表,适合对实时性要求不高、能接受秒级延迟的场景 - 坚持用 MySQL XA:仅限单 MySQL 实例多 database,且能接受运维复杂度——DBA 得定期巡检
XA RECOVER输出,手动干预卡住事务
真正容易被忽略的,是 undo_log 表的主键冲突或磁盘满导致写入失败——它不会抛异常,只会静默跳过日志记录,结果就是全局回滚时找不到快照,直接失败。上线前务必压测并检查该表的写入成功率。











