应直接查 INFORMATION_SCHEMA.INNODB_TRX 而非 SHOW PROCESSLIST,因后者仅显示连接状态,无法识别持锁的 Sleep 长事务;INNODB_TRX 提供 TRX_STARTED、TRX_STATE 和 TRX_MYSQL_THREAD_ID 等关键字段,可精准定位阻塞源、计算真实运行时长并安全 Kill。

查 INNODB_TRX 而不是 SHOW PROCESSLIST
SHOW PROCESSLIST 只显示连接状态,90% 的阻塞型长事务藏在 Command='Sleep'、State=NULL 但 TRX_STATE='RUNNING' 的“假空闲”连接里。真正持锁、卡住 DDL 或阻塞核心业务的,几乎都得靠 INFORMATION_SCHEMA.INNODB_TRX 定位。
重点查这三列:
-
TRX_STARTED:用TIMESTAMPDIFF(SECOND, TRX_STARTED, NOW())算真实运行秒数,别信PROCESSLIST.TIME -
TRX_STATE = 'RUNNING':说明事务既没提交也没回滚,资源仍被占用 -
TRX_MYSQL_THREAD_ID:这才是KILL的目标,和PROCESSLIST.ID不一定相等
加条件过滤更稳妥:WHERE TRX_STATE = 'RUNNING' AND TIMESTAMPDIFF(SECOND, TRX_STARTED, NOW()) > 30(生产建议阈值设为 30 秒,不是 10 分钟)
KILL CONNECTION 还是 KILL TRANSACTION?看 MySQL 版本和锁角色
MySQL 5.7+ 支持 KILL TRANSACTION <code>TRX_ID,它只终止事务本身,连接保活,适合连接池场景;5.6 及更早版本只能用 KILL QUERY <code>ID,但它不结束事务,TRX_STATE 仍为 RUNNING 或 LOCK WAIT,锁不会释放。
严禁对等待方(即 TRX_WAITING_TRX_ID IS NOT NULL)执行 KILL CONNECTION——这会让持锁者继续跑,其他请求照堵。
正确顺序是:
- 从
INNODB_TRX找出TRX_MYSQL_THREAD_ID - 关联
performance_schema.threads查PROCESSLIST_USER和PROCESSLIST_INFO留痕 - 再执行
KILL CONNECTION <code>thread_id(持锁或 Sleep 长事务)或KILL QUERY <code>thread_id(仅中断慢语句)
KILL 后锁还在?不是命令失败,是回滚在后台跑
执行 KILL 后 SHOW PROCESSLIST 显示状态为 Killed 是正常现象,不代表锁已释放。InnoDB 必须按 undo log 逆向回滚,耗时 ≈ 正向操作的 3~5 倍。
小事务(几行更新)通常 100ms 内完成;大事务(如百万行 UPDATE)可能卡在 Rolling back 状态数分钟,期间锁仍被占用。
若发现 kill 后锁“一直不放”,先查 INNODB_TRX 中该事务的 TRX_OPERATION_STATE 是否为 rolling back,再看 TRX_ROWS_MODIFIED 估算剩余工作量。
为什么不能跳过排查直接批量 KILL?
手动写脚本轮询 INNODB_TRX + KILL 风险集中:
- 并发下可能刚查完就提交了,
KILL却误杀 - 脚本若拼接用户输入,有 SQL 注入隐患
-
KILL后未检查是否真退出,下次又重复执行
更稳妥的是用 pt-kill --busy-time 30 --match-command Query --victim all --kill,它内置竞争规避、连接重试、日志审计,还能配合 --print 先预演。人工干预前,务必先用 SELECT * FROM performance_schema.threads WHERE PROCESSLIST_ID = ? 查出对应应用用户和 SQL,留痕备查。
最容易被忽略的是:kill 后必须立刻验证回滚是否完成——查 information_schema.innodb_trx 是否清空,而不是只看 processlist 里有没有那个 ID。因为线程 ID 可能被复用,而事务残留会导致后续 DDL(如 TRUNCATE、ALTER TABLE)持续阻塞。


















