可通过查询 INFORMATION_SCHEMA.INNODB_TRX 发现悬挂事务:trx_state = 'RUNNING' 且 trx_query IS NULL 或为空,同时 trx_started 时间远超业务正常耗时(如 >30s),表明客户端异常断连后事务未释放。

怎么发现 MySQL 里有悬挂事务(zombie transaction)
MySQL 自身不会主动标记“这是悬挂事务”,但你可以通过 INFORMATION_SCHEMA.INNODB_TRX 查到长时间未提交、状态卡在 RUNNING 却没活跃 SQL 的事务。常见表现是:trx_started 时间很老,trx_state = 'RUNNING',但 trx_query IS NULL 或只显示 NULL —— 这往往就是网络断开后客户端没来得及 rollback,服务端还挂着的“僵尸”。
- 执行
SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id, trx_query FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_state = 'RUNNING' AND (trx_query IS NULL OR trx_query = '') ORDER BY trx_started; - 重点关注
trx_started超过你业务正常事务耗时(比如 >30s)的记录 - 注意:
trx_mysql_thread_id对应PROCESSLIST中的ID,可用来确认连接是否已断(查SHOW PROCESSLIST看该 ID 是否还在)
如何安全 kill 悬挂事务而不引发锁冲突
直接 KILL 线程 ID 可能失败(比如线程已断连但事务未释放),更可靠的方式是用 innodb_rollback_on_timeout 配合超时机制,或手动触发回滚。但要注意:MySQL 5.7+ 不支持对 trx_state = 'RUNNING' 且无 query 的事务直接 ROLLBACK TRX_ID,只能 kill 线程或等它自己超时。
- 先确认线程是否还活着:
SELECT ID, COMMAND, TIME, STATE FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = <code>xxx;;如果COMMAND = 'Sleep'且TIME > 300,基本可 kill - 执行
KILL <code>xxx;(不是KILL QUERY),这会终止连接并触发事务回滚 - 如果
KILL后INNODB_TRX里仍存在该事务,说明回滚慢(大事务、磁盘 I/O 压力大),需耐心等待,不要重复 kill - 避免在高并发写场景下批量 kill,可能引发大量 undo 回滚争抢,拖慢整个实例
预防悬挂事务:客户端和服务端双侧配置要点
靠事后清理不如提前防住。悬挂事务本质是客户端异常退出 + 服务端无感知,所以必须让双方都有“超时即放弃”的契约。
- 客户端连接池必须设
connectionTimeout和socketTimeout(如 JDBC 的connectTimeout/socketTimeout),否则网络中断后连接会一直 hang 在池里 - MySQL 侧开启
wait_timeout(默认 28800 秒)和interactive_timeout,但这个只管空闲连接,不管活跃事务;真正管事务生命周期的是innodb_lock_wait_timeout(默认 50 秒),但它只影响锁等待,不杀长事务 - 关键配置:
max_execution_time(MySQL 5.7.8+)可限制单条语句执行时间,配合应用层事务拆分,避免大事务卡死 - 应用代码里所有
begin必须配对commit/rollback,且放在finally或使用 try-with-resources,不能依赖连接关闭自动回滚
为什么 show processlist 看不到但 INNODB_TRX 还有记录
这是最让人困惑的现象:SHOW PROCESSLIST 里找不到对应线程,但 INNODB_TRX 里事务还挂着。根本原因是:连接已断(TCP RST 或 FIN),MySQL 线程被回收,但 InnoDB 层的事务结构还没清理完——尤其当它正持有锁、或正在回滚大 undo log。
- 这种情况通常持续几秒到几分钟,取决于 undo 量和 I/O 负载;极端情况下(比如几十 GB 回滚日志)可能卡住十几分钟
-
INNODB_TRX.trx_state仍为'RUNNING'是误导,实际已进入“隐式回滚中”,只是状态没及时刷新 - 此时唯一能做的就是等,或者重启 MySQL(不推荐,除非完全卡死);强行 kill mysqld 可能导致 crash recovery 时间极长
- 监控上建议加告警:连续 2 分钟
SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_state = 'RUNNING' AND trx_query IS NULL> 5,就人工介入查因
INNODB_TRX 里那条记录消失——而这个动作可能滞后于网络中断数秒甚至更久,这点很容易被忽略。










