MySQL分库分表后本地事务天然失效,因InnoDB的undo/redo log仅限单实例,跨库操作无全局回滚锚点;@Transactional仅作用于当前数据源,无法协调多库事务状态,网络与节点故障进一步导致原子性降级为概率问题。

MySQL本地事务机制在分库分表下天然失效
单库MySQL靠InnoDB的undo log和redo log实现ACID,但这些日志只存在于单个实例内部。一旦数据被拆到db_order_0、db_order_1等不同物理库,每个库的事务日志互不感知——orderDao.insert()提交了,inventoryDao.deductStock()还没执行完,就没有全局回滚锚点。
你写的@Transactional注解在Spring里只管当前数据源,对跨库操作完全无感;它不会去协调另一个MySQL实例的事务状态,也不会读取对方的binlog或undo log。
网络与节点故障让“原子性”变成概率问题
分布式事务要求所有参与者“同时成功或同时失败”,但现实是:网络可能丢包、超时、抖动;某个MySQL实例可能在prepare阶段响应了,却在commit指令到达前宕机;协调者(比如Seata Server)自己挂了,参与者就卡在“半提交”状态,既不能确认也不能回滚。
常见错误现象包括:
- 用户看到订单已生成,但库存没扣(
order库提交成功,inventory库因网络超时未收到commit) - 数据库监控里出现大量
XA_RECOVERING状态连接,说明2PC卡在中间态 - 主从延迟叠加网络延迟,导致
prepare阶段耗时超过应用层超时阈值,触发误判回滚
两阶段提交(2PC)不是银弹,而是性能与可用性的折中
MySQL原生支持XA START/END/PREPARE/COMMIT,但生产环境极少直接用,原因很实在:
-
prepare阶段必须加锁并写日志,阻塞其他事务,QPS一高就雪崩 - 协调者单点:如果
mysql -h coordinator挂了,所有待决事务冻结,人工介入成本极高 - MySQL 8.0+虽优化了
XA性能,但依然无法规避网络分区下的脑裂风险——两个库各自认为自己是“唯一提交成功方” - Binlog与
XA日志不同步:MySQL 9.6.0把外键上移到SQL层后,XA事务的Binlog记录顺序和引擎层实际提交顺序可能错位,CDC同步会丢变更
真正落地时,业务一致性比ACID更关键
硬扛强一致,往往换来的是超时、降级、人工对账。实际项目中更常见的做法是接受短暂不一致,用业务逻辑兜底:
- 下单时先写
order库生成预订单(状态为PENDING),再发MQ通知库存服务;库存扣减成功后,才异步更新订单为CONFIRMED - 所有跨库操作都带
business_id和version,失败时靠定时任务扫描PENDING订单+重试+告警 - 避免在事务内调用远程HTTP或RPC——网络调用不可回滚,必须转成消息或本地表落库
最易被忽略的一点:分库分表后,连SELECT ... FOR UPDATE都可能跨不到同一分片,你以为的“加锁保护”其实根本没生效。


















