老主库未提交事务在切换瞬间直接丢失,mysql不会回滚、不记录、不补偿——它彻底蒸发;因kill-9强杀导致innodb来不及触发任何清理,undo_log、buffer pool、redo log中未commit数据全部丢弃,仅已commit且ack的binlog被从库同步,应用层必须通过幂等设计、状态表或异步消息自行兜底。

老主库未提交事务在切换瞬间直接丢失,MySQL 不会回滚、不记录、不补偿——它彻底蒸发,应用层必须自己兜底。
未提交事务在 kill -9 后为什么无法恢复
MySQL 进程被 kill -9 时,连接强制断开,InnoDB 来不及触发任何清理逻辑:innodb_rollback_on_timeout 不生效,wait_timeout 来不及触发,UNDO_LOG 中的回滚段、Buffer Pool 中的脏页、redo log 中已写未 commit 的记录,全部随进程终止而丢弃。从库只同步了已 COMMIT 并成功 ACK 的 binlog,这部分能保全;其余未 commit 的变更,在主库侧物理消失。
常见错误认知是“MySQL 会自动回滚”或“undo log 能跨实例恢复”,实际上:INFORMATION_SCHEMA.INNODB_TRX 在进程死后清空,SHOW ENGINE INNODB STATUS 无法还原,gtid_executed 集合里也绝不会有未 commit 的事务 ID。
切换前如何识别并清理未提交事务
不能等切换完成再处理——必须在停写前主动发现并干预。关键检查点:
- 执行
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED,重点关注TRX_STATE = 'RUNNING'且TRX_STARTED时间远早于当前时间的记录 - 结合
information_schema.PROCESSLIST查trx_mysql_thread_id对应的USER、HOST和COMMAND,排除监控/DBA 工具类连接 - 对疑似卡住的事务(如
trx_query IS NULL且运行超 60 秒),用KILL [thread_id]终止(注意:杀的是线程 ID,不是trx_id) - 杀完后立刻查
INNODB_TRX是否清空——不要只看PROCESSLIST,因为线程 ID 可能被复用,残留事务会阻塞后续 DDL
应用层必须实现幂等补偿,而不是依赖 MySQL
MySQL 本身不提供“未提交事务补偿机制”。所谓补偿,本质是业务语义层面的兜底设计,常见可靠做法:
- 在事务外先落状态:例如支付场景,扣款前插入
payment_pending记录,状态为pending;事务成功则更新为success;切换后扫描超时 pending 记录,调用查单接口确认终态 - 用异步消息解耦:本地事务提交后发 MQ 消息,消费端幂等执行;即使主库崩溃,消息仍可被重投,新主库上能继续处理
- 避免
SELECT FOR UPDATE+ 外部回调的模式:锁无法跨主库延续,切换后锁失效,但业务逻辑可能卡在中间态,导致数据不一致
最容易被忽略的是:补偿逻辑必须覆盖“客户端收到 Lost connection to MySQL server during query 后无重试”的场景——此时应用根本不知道事务是否成功,只能靠状态表或消息最终一致性来判定。











