当前有无死锁发生可直接执行show engine innodb status,若输出含“latest detected deadlock”则说明近1小时内发生过死锁(已自动回滚),该字段仅保留最后一次死锁上下文,重启后清空,需process权限,且输出为非结构化文本。

怎么看当前有没有死锁发生
直接执行 SHOW ENGINE INNODB STATUS,输出里只要出现 “LATEST DETECTED DEADLOCK” 就说明最近 1 小时内发生过死锁——注意不是“正在死锁”,而是“刚被检测到并已自动回滚一个事务”。这个字段只保留最后一次死锁的完整上下文,再往前的就没了。
常见错误现象:反复执行命令却看不到该段落,误以为没发生死锁;其实可能是死锁刚被处理完、还没新发生,或者压根没触发(比如事务太短、隔离级别低、没竞争同一行)。
- 必须用具备
PROCESS权限的账号执行,普通只读账号会静默失败(无报错,但输出不包含 INNODB STATUS) - 输出是纯文本块,没有结构化字段,得靠人眼定位 “LATEST DETECTED DEADLOCK” 起始位置
- 如果实例重启过,这块记录就清空了,不能追溯历史
死锁日志里哪些字段真正有用
重点盯三块:TRANSACTION 行(事务 ID 和状态)、mysql tables in use(涉及表)、以及每个事务下面的 WAITING FOR THIS LOCK TO BE GRANTED 和 HOLDS THE LOCK(S)。
使用场景:快速判断是不是两个事务在争同一行的 X 锁,还是跨表、跨索引甚至间隙锁(gap lock)引发的连锁等待。
-
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:看它等的是哪个索引、哪条记录(如PRIMARY索引上id=123),再往下找*** (2) HOLDS THE LOCK(S):对应的记录是否一致 - 如果看到
lock_mode X locks gap before rec insert intention waiting,基本能确定是插入时的间隙锁冲突,和唯一键无关,和索引范围有关 - 事务开头的
ACTIVE 34 sec表示它卡在这儿 34 秒了——说明应用层没设超时或没捕获异常,导致事务挂太久
为什么 SHOW ENGINE INNODB STATUS 不够用
它只给快照,不记录过程,也没时间戳精度(只到秒级),更没法聚合统计。线上一旦高频死锁,靠人工刷这个命令根本来不及响应。
性能与兼容性影响:频繁执行该命令本身开销不大,但输出体积可能达几 MB(尤其大事务多时),网络传输和解析成本高;MySQL 8.0+ 虽支持 performance_schema.data_locks,但默认关闭且需额外权限配置。
- 无法区分是偶发 bug 还是设计缺陷:比如批量导入时固定顺序加锁就能避免,但日志里看不出加锁顺序逻辑
- 查不到应用端线程 ID、SQL 原始语句(只有 digest hash)、客户端 IP,难以反向定位代码位置
- 从 MySQL 5.7 开始,
innodb_print_all_deadlocks = ON可把每次死锁写进 error log,但默认关着,且 error log 通常不集中收集
怎么低成本补上监控缺口
最务实的做法是结合 innodb_print_all_deadlocks + 日志采集工具(如 filebeat),再配个简单告警规则:每分钟 error log 出现 “Deadlock found when trying to get lock” 超过 2 次就通知。
参数差异:该配置在 my.cnf 里设置后需重启 MySQL(5.7+ 支持动态修改,但建议重启确认生效);开启后每条死锁都会追加到 error log,内容比 SHOW ENGINE INNODB STATUS 更精简,但少了事务堆栈细节。
- 别依赖慢查询日志抓死锁 SQL——死锁里的语句未必“慢”,可能毫秒级就卡住
- 应用层要捕获
Deadlock found when trying to get lock这个错误码(MySQL 错误号 1213),而不是只打 info 日志 - 测试阶段用
SELECT * FROM performance_schema.data_lock_waits查实时阻塞链,但生产环境慎用,可能加重锁竞争
死锁不是非得“消灭”,而是得知道它在哪、为什么来、来了之后有没有被正确消化。日志开了、告警通了、应用重试逻辑写了,剩下就是看业务能不能接受那个重试窗口——这点最容易被忽略。











