未提交事务在主库被kill -9后直接丢失且无法恢复,因innodb来不及执行回滚或刷盘;半同步和sync_binlog仅保障已提交事务一致性;补偿必须由应用层通过幂等设计、状态表或异步消息实现。

主库在高可用切换瞬间宕机或被强制 kill -9,未提交事务不会同步到从库,也不会自动“补偿”——它直接丢失,且没有回滚动作可追溯。
kill -9 主库后,未提交事务既不回滚也不复制
MySQL 服务进程被 kill -9 强杀时,连接未正常关闭,innodb_rollback_on_timeout 不生效,wait_timeout 也来不及触发。InnoDB 来不及执行任何清理,redo log 中已写但未 commit 的记录、Buffer Pool 中的脏页、undo log 中的回滚段,全部随进程终止而丢弃。从库只收到了已 commit 且成功 ACK 的 binlog,这部分事务能保全;其余未 commit 的修改,在主库侧彻底蒸发。
- 对比
shutdown:后者会等待活跃事务完成或回滚,再关闭,更安全 - 半同步(
rpl_semi_sync_master_wait_point=AFTER_SYNC)只保障已 commit 事务的强一致性,对未 commit 事务无保护能力 - 客户端收到
Lost connection to MySQL server during query后,应用层若无重试+幂等设计,这笔操作就真正“消失”了
故障转移后无法靠 MySQL 自身恢复未提交状态
切换完成后,新主库(原从库)的数据快照是上一次成功 apply 的 GTID 或 position 对应的状态,它不包含任何主库崩溃前未 commit 的变更。MySQL 没有“事务补偿日志”或“未提交事务快照”机制,INFORMATION_SCHEMA.INNODB_TRX 在原主库进程死后清空,SHOW ENGINE INNODB STATUS 也无法还原。
- 不能依赖
UNDO_LOG恢复:undo log 是为 rollback 服务的,不是为跨实例状态同步设计的 - GTID 只标记已提交事务,
gtid_executed集合里不会有未 commit 的事务 ID - 即使开启
binlog_format=ROW+log_slave_updates,从库 binlog 里也只记录已 commit 的 event
真正的补偿必须由应用层实现
所谓“补偿”,本质是业务语义层面的兜底,MySQL 不参与也不感知。常见做法是在事务边界外引入异步消息或状态表,把未提交操作的意图先落库/发消息,再执行本地事务;切换后通过定时任务比对状态并重放或修正。
- 例如:支付扣款前先插入
payment_pending记录,状态为pending;事务成功则更新为success;切换后扫描超时pending记录,调用查单接口确认终态 - 避免用
SELECT ... FOR UPDATE锁住行再等外部回调,这会阻塞且无法跨主库延续锁 - 不要依赖
innodb_lock_wait_timeout触发自动回滚来“释放资源”——切换时锁早已随连接释放,但业务状态已错乱
最易被忽略的一点:很多团队以为开了半同步 + sync_binlog=1 就万事大吉,却没意识到,这些参数只约束“已 commit”的路径。未走到 commit 那一步的事务,就像没按过电梯按钮——系统根本不知道你要去几楼。











