innodb_status 中有无死锁需查看“latest detected deadlock”章节,若不存在则说明最近未发生或已被覆盖;开启innodb_print_all_deadlocks=on可将所有死锁写入error log。

怎么看 INNODB_STATUS 里有没有死锁
死锁不是每次都会报错给应用,MySQL 通常只在事务被选为 victim 时抛出 Deadlock found when trying to get lock,但真正频繁发生的死锁,得翻 SHOW ENGINE INNODB STATUS 的输出。它每执行一次,会刷新最近一次死锁的完整上下文——注意:**只保留最后一次**,不是历史记录。
实操建议:
- 立刻执行
SHOW ENGINE INNODB STATUS\G(加\G是为了可读性),重点找*** (1) WAITING FOR THIS LOCK TO BE GRANTED:和*** (2) HOLDS THE LOCK(S):这两块 - 如果输出里压根没有
LATEST DETECTED DEADLOCK这个章节,说明最近没触发死锁(或者刚被新死锁覆盖了) - 别依赖慢查询日志或 general_log 捕捉死锁——它们不记录锁等待链,信息不全
用什么方式持续捕获死锁日志
SHOW ENGINE INNODB STATUS 是瞬态快照,手动查等于守株待兔。真要监控频率,得靠定时采集 + 差异比对。
实操建议:
- 写个简单脚本,每 10–30 秒执行一次
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep -A 20 "LATEST DETECTED DEADLOCK",把非空结果追加到文件 - 注意:不要用
mysqldumpslow或pt-query-digest解析这个输出——它们不识别 INNODB_STATUS 格式,会误解析 - 如果 MySQL 版本 ≥ 5.6.2,可以开启
innodb_print_all_deadlocks=ON(需重启),这时所有死锁都会写进 error log,路径由log_error配置项决定,比轮询SHOW ENGINE更可靠
死锁日志里哪些字段最关键
一份典型的死锁日志里,90% 的信息是干扰项。盯住这三处,就能快速定位冲突源头:
实操建议:
- 看事务持有的锁:
*** (1) HOLDS THE LOCK(S):后面的RECORD LOCKS space id ...行,记下space id和page number,再用SELECT NAME FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE SPACE = ?反查表名 - 看 SQL 语句:
mysql tables in use 1, locked 1下面紧跟着的UPDATE ... WHERE或INSERT ... SELECT—— 注意它未必是应用层发的原始语句,可能是触发器或外键隐式执行的 - 看锁类型:
lock_mode X locks rec but not gap是行锁,lock_mode S locks gap before rec是间隙锁,后者常出现在范围条件(如WHERE id > 100)中,容易和插入冲突
为什么开了 innodb_print_all_deadlocks 还看不到死锁
开了配置却没日志,大概率不是配置没生效,而是 error log 本身被截断、权限不足,或 MySQL 没有写入权限。
实操建议:
- 确认配置已加载:
SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME = 'innodb_print_all_deadlocks',返回ON才算生效 - 检查 error log 路径是否可写:
SELECT @@log_error,然后ls -l $(cat /path/to/mysql/error.log)看属主和权限 - 某些云数据库(如阿里云 RDS、腾讯云 CDB)默认屏蔽该变量,或仅允许通过控制台开启,直接改 my.cnf 无效
- error log 文件过大时,MySQL 可能自动 rotate 并清空旧内容,建议搭配 logrotate 配置保留最近 7 天
死锁分析最耗时间的从来不是日志怎么抓,而是从一堆 space id 和 page number 里还原出具体哪张表、哪个索引、哪几行数据在争抢——这一步没法全自动,得人工交叉验证。











