应优先查询INNODB_TRX表,关注TRX_STATE(LOCK WAIT或长时间RUNNING)、TRX_STARTED、TRX_QUERY及TRX_MYSQL_THREAD_ID,再结合INNODB_LOCK_WAITS和SHOW PROCESSLIST定位阻塞源头。

查长查询:别只看 PROCESSLIST,得盯住 INNODB_TRX 和 STATE
直接 SHOW PROCESSLIST 或查 information_schema.PROCESSLIST 很容易漏掉真正在持锁的长查询——尤其是那些 COMMAND 是 Query 但 STATE 为 Locked、Sending data 或 Updating 的会话。真正危险的是已开始执行、尚未提交、且持有行锁/间隙锁的事务,它们藏在 INFORMATION_SCHEMA.INNODB_TRX 里。
必须联合查三张表才能准确定位:
-
INFORMATION_SCHEMA.INNODB_TRX:看TRX_STARTED(超 30 秒就该警惕)、TRX_STATE(RUNNING或LOCK WAIT)、TRX_ROWS_MODIFIED(越大回滚越慢) -
INFORMATION_SCHEMA.INNODB_LOCK_WAITS:确认是否被阻塞,找出BLOCKING_TRX_ID -
INFORMATION_SCHEMA.PROCESSLIST:用TRX_MYSQL_THREAD_ID关联出ID、USER、INFO,看清实际执行的 SQL
INFO 字段默认截断(1024 字符),关键语句可能看不到,别光靠它判断;STATE 为 Locked 不一定异常,得结合 TRX_ROWS_LOCKED > 1000 和业务场景综合判断。
KILL TRANSACTION 还是 KILL QUERY?版本和锁类型决定一切
MySQL 5.7+ 支持 KILL TRANSACTION <code>TRX_ID,这是最安全的选择:它只终止事务本身,连接保持活跃,适合 HikariCP、Druid 等连接池场景。5.6 及更早版本不支持该语法,只能退而求其次用 KILL QUERY <code>ID ——但它只中断当前语句,事务状态仍为 RUNNING,锁不会释放,必须后续显式执行 ROLLBACK。
绝对不能对等待方(TRX_STATE = 'LOCK WAIT')执行 KILL <code>ID(即 KILL CONNECTION):这会让持锁者继续运行,其他请求照堵,问题反而恶化。
正确顺序是:
- 从
INNODB_LOCK_WAITS找出BLOCKING_TRX_ID - 关联
INNODB_TRX拿到对应TRX_MYSQL_THREAD_ID - 再执行
KILL TRANSACTION <code>TRX_ID或KILL QUERY <code>ID
KILL 后锁还在?不是命令失败,是回滚卡住了
执行 KILL 后,SHOW PROCESSLIST 显示状态为 Killed 是正常现象,不代表锁已释放。InnoDB 必须按 undo log 逆向回滚,耗时通常是正向操作的 3~5 倍。
小事务(几行更新)一般 100ms 内完成;百万级 UPDATE 可能卡在 Rolling back 状态数分钟。此时锁仍被占用,新请求继续排队。
验证是否真在回滚:
- 查
INNODB_TRX:若TRX_STATE = 'ROLLING BACK'且TRX_STARTED时间很早,说明回滚中 - 看
TRX_ROWS_MODIFIED:数值越大,剩余工作量越多 - 别等太久就重启 mysqld——这会导致数据不一致,生产环境严禁这么做
为什么不能写脚本批量 KILL?并发和权限风险比你想象的高
手写定时脚本轮询 INNODB_TRX + 拼接 KILL 语句,看似高效,实则埋雷:
- 刚查完
TRX_ID,事务就提交了,KILL却误杀一个已结束的连接 - 脚本若拼接用户输入(比如按
USER过滤),存在 SQL 注入风险 -
KILL后没检查是否真退出,下次又重复执行,形成雪球效应
更稳妥的做法是用 pt-kill:pt-kill --busy-time 30 --match-command Query --victim all --kill --print
先加 --print 预演,确认目标无误后再去 --kill。它内置重试、连接复用、竞争规避,还能自动过滤系统账号和本地管理连接。
人工干预前,务必用 SELECT * FROM performance_schema.threads WHERE PROCESSLIST_ID = ? 查出对应应用用户和完整 SQL,留痕备查——这点常被跳过,但出事后就是关键证据。


















