查INNODB_TRX可快速定位长时间未提交事务,重点关注TRX_STARTED时间早于当前30分钟且TRX_STATE为RUNNING或LOCK WAIT的记录,结合PROCESSLIST确认线程状态,TRX_MYSQL_THREAD_ID为KILL关键ID,需区分真实运行事务与僵尸事务并谨慎处理。

查 INFORMATION_SCHEMA.INNODB_TRX 看长时间未提交的事务
MySQL 的 INNODB_TRX 表会实时记录所有活跃的 InnoDB 事务,其中 TRX_STATE 为 LOCK WAIT 表示正卡在锁上,而 TRX_STARTED 时间过早(比如 > 30 分钟)但状态仍是 RUNNING 或 LOCK WAIT 的,大概率是应用崩溃、连接异常断开后遗留的僵尸事务。
执行以下查询快速定位可疑事务:
SELECT
TRX_ID,
TRX_MYSQL_THREAD_ID AS thread_id,
TRX_QUERY,
TRX_STATE,
TIMEDIFF(NOW(), TRX_STARTED) AS duration,
TRX_WAITING_LOCK_ID
FROM INFORMATION_SCHEMA.INNODB_TRX
WHERE TRX_STARTED < NOW() - INTERVAL 30 MINUTE
AND TRX_STATE IN ('RUNNING', 'LOCK WAIT');注意:TRX_MYSQL_THREAD_ID 是 kill 操作所需的关键 ID;TRX_QUERY 可能为 NULL(比如事务只执行了 BEGIN 未发 SQL),不能仅凭它判断是否“有操作”。
结合 INFORMATION_SCHEMA.PROCESSLIST 判断连接是否已失效
一个事务存在,但对应连接可能早已断开(比如客户端 crash、网络中断、超时关闭),这时 PROCESSLIST 中该线程状态会是 Sleep 且 COMMAND 为 Sleep,但 TIME 值极大(远超 wait_timeout),或 INFO 为空 —— 这类线程无法响应 KILL,却仍持有锁。
关联查询确认:
SELECT t.TRX_ID, p.ID AS processlist_id, p.USER, p.HOST, p.DB, p.COMMAND, p.TIME AS process_time, p.STATE, p.INFO FROM INFORMATION_SCHEMA.INNODB_TRX t LEFT JOIN INFORMATION_SCHEMA.PROCESSLIST p ON t.TRX_MYSQL_THREAD_ID = p.ID WHERE t.TRX_STARTED < NOW() - INTERVAL 30 MINUTE;
重点关注:COMMAND 是 Sleep 且 process_time > 300(5 分钟),同时 STATE 为空或为 Waiting for table flush 等非活跃态 —— 这类基本可判定为僵尸。
KILL 之前务必确认是否真可终止
直接 KILL <thread_id> 对某些僵尸事务无效(尤其连接已断),但对仍在运行的长事务又可能引发回滚风暴。必须分情况处理:
- 如果
PROCESSLIST中对应线程状态是Query且INFO显示真实 SQL(如UPDATE ... WHERE ...),说明它还在跑,KILL 后会触发回滚 —— 先评估该 SQL 影响行数和回滚耗时 - 如果线程状态是
Sleep且TIME > wait_timeout(默认 28800 秒),MySQL 本该自动清理,未清理说明可能被阻塞在锁链中,此时 KILL 通常无响应,需从上游查起(比如是否有未提交的分布式事务、XA 事务残留) - 若
TRX_STATE = 'LOCK WAIT'且TRX_WAITING_LOCK_ID非空,用INFORMATION_SCHEMA.INNODB_LOCK_WAITS查谁在持锁 —— 僵尸事务往往不是锁源,而是锁受害者
预防比检测更重要:应用层必须设事务超时
MySQL 本身不提供“事务级自动超时”机制(innodb_lock_wait_timeout 只控制锁等待时间,不杀事务本身),所以僵尸事务几乎全是应用侧问题:未捕获异常、未写 finally rollback、连接池配置不当等。
关键落地点:
- 所有显式
BEGIN后必须配对COMMIT或ROLLBACK,禁止裸写BEGIN - 应用代码里给数据库操作加业务级超时(如 Spring 的
@Transactional(timeout = 30)) - 连接池(如 HikariCP)开启
leakDetectionThreshold,及时发现未关闭的连接 - 定期跑脚本(如每 5 分钟)扫描
INNODB_TRX中TRX_STARTED < NOW() - INTERVAL 10 MINUTE的事务并告警,而不是等锁等待爆发
真正难处理的不是“怎么查”,而是当 TRX_STATE 显示 RUNNING 却 PROCESSLIST 找不到对应线程时——那说明内核态事务结构还在,但用户态连接彻底消失了,这种只能重启 mysqld 或等待其自行释放(取决于 innodb_fast_shutdown 设置)。


















