直接查看SHOW ENGINE INNODB STATUS\G输出中的LATEST DETECTED DEADLOCK区块即可准确定位死锁,它完整呈现两个事务的SQL、线程ID、持锁与等待锁关系、索引名及锁类型,是唯一实时、精准、零配置的排查入口。

直接看SHOW ENGINE INNODB STATUS\G输出里的LATEST DETECTED DEADLOCK段,它比错误日志更全、更及时——错误日志只记录被回滚的事务,而这里能看到两个事务的完整锁状态和SQL。
怎么看SHOW ENGINE INNODB STATUS里的死锁信息
执行命令后,跳到LATEST DETECTED DEADLOCK区块,重点抓三块:
-
TRANSACTION [id]:两个事务各自的ID(如TRANSACTION 12345),注意哪个被标记为WE ROLL BACK TRANSACTION (1)——这是MySQL选的“牺牲者” -
HOLDS THE LOCK(S):当前事务已持有的锁,格式类似RECORD LOCKS space id 88 page no 7 index `idx_user_id`,说明它锁住了idx_user_id索引上的某页 -
WAITING FOR THIS LOCK:该事务正在等什么锁,比如RECORD LOCKS space id 88 page no 3 index `PRIMARY`,说明它想锁主键,但被另一个事务占着
对照两事务的HOLDS和WAITING FOR,就能画出等待环:事务A持idx_user_id锁,等PRIMARY锁;事务B持PRIMARY锁,等idx_user_id锁——典型顺序不一致导致的循环等待。
错误日志里Deadlock found when trying to get lock怎么快速定位
错误日志(路径查SHOW VARIABLES LIKE 'log_error')里那行报错本身没用,关键在它之后紧跟着的几段日志:
- 找最近一次以
*** (1) TRANSACTION:开头的块,它对应第一个事务;*** (2) TRANSACTION:是第二个 - 每段里
UPDATE/DELETE语句就是实际触发死锁的SQL,注意WHERE条件——比如WHERE user_id = 5和WHERE id = 5看似一样,但锁的是不同索引 - 如果日志里出现
gap lock或next-key lock,说明涉及范围查询(如WHERE status IN ('pending', 'processing')),间隙锁扩大了冲突面
别只看SQL文本,要结合表结构判断:那个WHERE条件有没有走索引?没走索引就会升级成全表扫描+表级锁,死锁概率陡增。
为什么innodb_print_all_deadlocks = ON必须开
默认情况下MySQL只保留最后一次死锁记录,SHOW ENGINE INNODB STATUS只能看到最近一次;而生产环境往往连续发生多次,旧的就被覆盖了。
- 临时开启:
SET GLOBAL innodb_print_all_deadlocks = ON(重启失效) - 永久生效:在
my.cnf的[mysqld]段加innodb_print_all_deadlocks = 1,并确认log_error路径可写 - 不开这个,你看到的可能只是“冰山一角”,真实高频死锁场景根本捞不到完整链条
开了之后,每次死锁都会追加进错误日志,配合tail -f /var/log/mysql/error.log | grep -i deadlock能实时捕获,比等人报错再查快得多。
分析时最容易忽略的三个点
一是space id和page no——它们指向物理数据页,同一space id不同page no大概率是不同行,相同page no才真正在争同一组记录;
二是事务活跃时间(ACTIVE 2 sec这类字段),如果一个事务ACTIVE几十秒,说明它前面卡在应用层(比如HTTP调用没返回),锁早该释放却拖着,这不是SQL问题而是业务逻辑问题;
三是tables in use 1, locked 1这种统计,locked数远小于in use数,说明只锁了部分行——这时候得回头检查WHERE是否用了索引,否则locked会飙升。


















