MySQL不支持跨库全局事务,需依赖中间件(如ShardingSphere-Proxy)或Seata AT模式实现;前者透明兼容、路由需分库键,后者需应用改造且依赖binlog;Group Replication不解决分布式事务。

MySQL 本身不支持跨库全局事务,得靠中间件补能力
MySQL 的 START TRANSACTION 只在单实例内生效,一旦业务拆到多个 MySQL 实例(比如按用户 ID 分库),XA START 虽然存在,但生产环境几乎没人用——协调器单点、超时机制弱、死锁排查难,挂一次就可能卡住整个资金链。
所以真实方案是:把全局事务语义交给中间件承担,MySQL 只负责执行本地事务。中间件不是透明代理,它要解析 SQL、管理分支事务状态、驱动二阶段提交(2PC)或最终一致性补偿。
ShardingSphere-Proxy 是目前最稳的落地选择
它把分片逻辑和事务协调全收在服务端,应用层无感知,兼容原生 JDBC/MySQL 协议,连 spring.datasource.url 都不用改,只换 host 和 port 就能接入。
关键配置注意三点:
-
transaction.type必须设为XA或BASE(Seata 集成时用BASE) - 分库键(如
user_id)必须出现在所有跨库事务的 WHERE 条件里,否则中间件无法路由到确定分片,直接报Cannot route to multiple data sources - 避免在事务中执行
SELECT ... FOR UPDATE跨分片,ShardingSphere 不支持跨节点加行锁,会降级成表锁甚至报错
Seata AT 模式 + MySQL binlog 是另一条路,但得动应用
它不依赖中间件做 SQL 解析,而是靠数据源代理,在本地事务提交前生成 undo_log,再由 Seata Server 协调各分支回滚。好处是分库逻辑完全由应用控制;坏处是每个服务都要引入 seata-spring-boot-starter,且必须确保 MySQL 开启 binlog_format=ROW,否则 undo_log 补偿会写错数据。
常见翻车点:
- 应用用了 HikariCP 连接池,没配
allowMultiQueries=true,导致 Seata 自动插入undo_log失败,错误信息是java.sql.SQLException: Can not issue data manipulation statements with executeQuery() - 事务方法里调了 Feign 客户端,但没开启
@GlobalTransactional,结果本地 MySQL 提交了,远程服务没调通,数据就永久不一致 - MySQL 表没主键,Seata 解析 binlog 时找不到唯一标识行,补偿更新会误改其他行
别碰 MySQL Group Replication 做全局事务
它解决的是高可用和强同步,不是分布式事务。GR 内部用的是基于 Paxos 的组通信协议,所有写请求必须被多数派确认才能提交,但它不提供跨节点事务原子性保证——你在一个节点上 INSERT,另一个节点上 UPDATE,这两个操作仍是独立事务,失败一个,另一个不会自动回滚。
真有人试过用 GR 搭配 XA COMMIT,结果发现:XA RECOVER 查不到跨节点的 XA 事务,因为每个节点只管自己的 XA 分支,没有全局协调者。最后都退回中间件方案。
跨库事务最难的从来不是技术选型,而是业务是否真的需要强一致性。很多所谓“全局事务”,其实用异步消息 + 本地事务表 + 对账就能扛住,反而更稳。要不要上中间件,先问一句:这笔操作失败后,人工能对得齐账吗?


















