应查INFORMATION_SCHEMA.INNODB_LOCK_WAITS获取阻塞关系,关联INNODB_TRX和PROCESSLIST定位活跃阻塞会话,排除复制/备份等关键线程后,再安全KILL;SHOW ENGINE INNODB STATUS仅保留最后一次死锁快照,无法反映持续阻塞状态。

如何识别 MySQL 中的死锁连接
MySQL 本身不直接暴露“死锁连接”,真正要查的是被阻塞的事务和持有锁却不释放的会话。关键看 information_schema.INNODB_TRX、INNODB_LOCK_WAITS 和 PROCESSLIST 三张表的联动结果。
常见错误现象:应用日志里反复出现 Lock wait timeout exceeded,但 SHOW PROCESSLIST 里没看到明显长时间运行的 Query;或者 SELECT * FROM information_schema.INNODB_TRX 显示 trx_state = "LOCK WAIT",且 trx_wait_started 时间远早于当前时间。
- 必须用具有
PROCESS和SELECT权限的账号连接(如root或专用监控用户) -
INNODB_LOCK_WAITS只记录当前正在等待的锁关系,不是历史快照;死锁检测由 InnoDB 自动触发并回滚其中一个事务,所以“死锁连接”其实是“尚未被自动处理的长期阻塞连接” - 不要只依赖
Time字段判断——PROCESSLIST中的Time是线程空闲/执行时长,不等于锁持有时间
Shell 脚本中怎么安全地查出阻塞者并 kill
不能直接 KILL 所有 State = "Locked" 的线程(那是 MyISAM 旧状态),也不能无差别 KILL trx_state = "RUNNING" 的事务——它可能只是慢查询,未必是锁源头。
核心逻辑是:找出 INNODB_LOCK_WAITS 中的 blocking_trx_id,反查 INNODB_TRX 得到对应 trx_mysql_thread_id,再关联 PROCESSLIST 确认是否活跃、是否有明确客户端 IP 和命令。
mysql -u$USER -p$PASS -Nse " SELECT DISTINCT p.ID FROM information_schema.INNODB_LOCK_WAITS w JOIN information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_id JOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id JOIN information_schema.PROCESSLIST p ON p.ID = b.trx_mysql_thread_id WHERE r.trx_state = 'LOCK WAIT' AND b.trx_state = 'RUNNING' AND p.COMMAND != 'Sleep' AND p.TIME > 60"
- 加
-Nse参数:禁用列名、严格模式、静默错误,避免脚本因空结果或警告中断 - 过滤
TIME > 60是为了排除刚建立就卡住的瞬时情况,避免误杀 - 务必先用
SELECT验证结果,再改成KILL——例如把上面语句开头换成SELECT CONCAT('KILL ', p.ID, ';')生成可审阅的 kill 命令
自动执行前必须加的防护措施
线上环境跑自动 kill 脚本,最怕的是 kill 掉主从复制线程、备份线程或管理后台连接。没有防护机制的脚本等于定时炸弹。
- 排除特定用户:
AND p.USER NOT IN ('repl', 'backup', 'admin_monitor') - 排除特定命令:
AND p.INFO NOT LIKE '%mysqldump%'、AND p.INFO NOT LIKE '%pt-heartbeat%' - 限制最大 kill 数量:用
LIMIT 3,防止一次干掉太多连接导致服务雪崩 - 写入操作日志:
echo "$(date): killing $(mysql_cmd)..." >> /var/log/mysql-killer.log,方便事后追溯 - 加锁文件防止并发执行:
[ -f /tmp/mysql-killer.lock ] && exit; touch /tmp/mysql-killer.lock; trap 'rm -f /tmp/mysql-killer.lock' EXIT
为什么不能只靠 SHOW ENGINE INNODB STATUS
这个命令输出里确实有 TRANSACTIONS 部分,包含最近死锁信息,但它只保留最后一次死锁详情,且输出是纯文本块,无法结构化提取阻塞者 ID。更关键的是:它不反映当前仍在持续阻塞的状态,只告诉你“刚才发生了什么”。
实际排查中,你遇到的往往不是“刚发生的死锁”,而是“已经卡住 5 分钟没人处理的阻塞链”。这时候 INNODB_LOCK_WAITS 是唯一能实时定位源头的依据。
另外注意:MySQL 5.7+ 默认关闭 innodb_status_output_locks,即使开了,输出也混在大段日志里,不适合 shell 解析;而 information_schema 表是稳定、可 JOIN、可 WHERE 的标准接口。
复杂点在于锁等待关系可能是多层嵌套的(A→B→C→D),脚本里只做一层阻塞者提取是稳妥的;真要递归分析,得用 Python 或 Perl 处理,shell 不适合。


















