应立即执行show engine innodb status\g,仅查看latest detected deadlock段,从中提取两个事务的transaction id、holds/waiting锁信息及末尾sql,结合innodb_print_all_deadlocks=1开启全量日志并配合logrotate轮转分析根因。

怎么看最近一次死锁现场?
执行 SHOW ENGINE INNODB STATUS\G,只盯住输出里 LATEST DETECTED DEADLOCK 这一段。它不保存历史,只留最后一次,所以发现报错就得立刻跑——等你连上服务器,可能已经覆盖三次了。
重点抓三块信息:
- 每个事务的
TRANSACTION行:记下id和mysql-thread-id,确认是不是你业务线的连接 - 每段里的
mysql tables in use和locked tables:看是否锁了同一张表、甚至同一索引 -
HOLDS THE LOCK(S)和WAITING FOR THIS LOCK TO BE GRANTED:谁先锁了哪行、谁在等哪行,直接暴露冲突本质。比如看到lock_mode X locks gap before rec,说明是间隙锁惹的祸
为什么查不到死锁日志?
默认 SHOW ENGINE INNODB STATUS 只保留最后一次,不是命令失效,是设计如此。如果线上报错频繁但日志里找不到上下文,大概率是 innodb_print_all_deadlocks 没开。
永久生效要改配置文件:[mysqld] 段下加:
innodb_print_all_deadlocks = 1 log_error = /var/log/mysql/error.log
注意两点:
- 开启后所有死锁都追加进 error log,不清理会撑爆磁盘——必须配
logrotate按天轮转,保留 7 天足够 -
log_error路径得确保 MySQL 进程有写权限,否则日志根本不会落地
怎么判断是不是真“频繁”?
别被日志刷屏带偏节奏。“频繁”的标准不是“每天报几次”,而是单位时间(比如每分钟)是否持续出现 3+ 次 ERROR 1213 (40001): Deadlock found when trying to get lock,且集中在同一张表、相似 SQL。
容易误判的情况:
- 应用层没做重试控制,一次失败就反复重发相同事务,日志里看起来高频,实际是单次死锁被放大
- 运维批量脚本(如定时归档)和业务流量叠加,导致短时尖峰,不是常态问题
- 只靠
INNODB STATUS统计频次——它压根不存历史,没法聚合
正确做法:用 performance_schema.data_locks + performance_schema.data_lock_waits(MySQL 8.0+)或定期采集 information_schema.INNODB_TRX 配合错误日志时间戳做聚合分析。
哪些参数影响死锁感知和处理?
innodb_deadlock_detect 默认就是 ON,关掉它只会让事务无限等待(直到 innodb_lock_wait_timeout 触发超时),反而更伤可用性。真正影响“感知频率”的是另外两个:
-
innodb_lock_wait_timeout:设太小(如 5 秒),会让本可等待成功的事务提前超时,被误记为“死锁相关失败”;设太大(如 300 秒),又拖慢故障暴露速度。建议值:30 -
innodb_rollback_on_timeout:MySQL 5.7+ 默认OFF,意味着超时事务不会自动回滚,残留锁可能引发后续真实死锁——这个才是隐蔽推手。必须显式设为ON
复杂点在于:死锁本身不是随机故障,而是多个事务以不一致顺序争抢行锁或间隙锁时必然出现的结果。排查时盯着 SQL 顺序、索引是否生效、事务是否过长,比调参数管用得多。











