直接查看show engine innodb status\g输出中的latest detected deadlock区块,即可准确定位互锁的两条sql、被回滚方及所涉索引;该区块位于分隔符之间,含 (1) transaction和 (2) transaction两段,需交叉比对holds the lock(s)与waiting for this lock to be granted以识别闭环等待关系。

直接看 SHOW ENGINE INNODB STATUS\G 输出里的 LATEST DETECTED DEADLOCK 区块,就能准确定位哪两条 SQL 互锁、谁被回滚、锁在哪个索引上——不需要查错误日志、也不用翻应用日志找报错堆栈。
怎么看 LATEST DETECTED DEADLOCK 里的两个事务块
输出中真正有用的部分被包裹在 ------------------------ LATEST DETECTED DEADLOCK ------------------------ 分隔符之间。每个事务以 *** (1) TRANSACTION 和 *** (2) TRANSACTION 开头,注意编号不表示执行先后,只表阅读顺序。
- 重点记下每段开头的
TRANSACTION xxxID 和末尾的query id xxx对应的 SQL(如update t set c = c + 1 where id = 2),这是冲突源头 -
MySQL thread id 12可用来关联information_schema.PROCESSLIST,确认该连接的用户、DB、命令类型 - 若 SQL 显示为
UPDATE ... WHERE ?这类带问号的模板,说明用了预编译语句,需同步比对应用日志中的实际参数
怎么从 HOLDS THE LOCK(S) 和 WAITING FOR THIS LOCK 推出锁冲突链
死锁本质是闭环等待:A 等 B 持有的锁,B 又等 A 持有的锁。必须交叉比对两段日志:
- 事务 (1) 的
WAITING FOR THIS LOCK TO BE GRANTED内容,要和事务 (2) 的HOLDS THE LOCK(S)是否匹配(比如都指向index `idx_account`) - 事务 (2) 的
WAITING FOR THIS LOCK同样要和事务 (1) 的HOLDS THE LOCK(S)对得上(比如都落在PRIMARY) - 若看到
lock_mode X locks gap before rec,基本可断定是 RR 隔离级别下范围查询 + 无索引引发的间隙锁交叉;全是rec but not gap,更可能是多表更新顺序不一致
WHERE 条件没走索引时怎么快速验证
死锁高发场景之一:UPDATE/DELETE 的 WHERE 条件未命中索引,InnoDB 退化为全表扫描并加大量行锁。
- 对出问题的 SQL 执行
EXPLAIN,检查type是否为ALL(全表扫描)、key是否为空 - 联合索引要满足最左前缀,
WHERE a = ? AND b > ?能用上(a,b),但WHERE b > ?就用不上 - 注意隐式类型转换:比如字段是
VARCHAR,却传入数字,会导致索引失效 -
ORDER BY + LIMIT类语句若没索引,也可能触发非预期锁范围扩展
为什么不能只依赖错误日志查死锁
MySQL 默认只把最后一次死锁写进错误日志(log_error),高频死锁时会覆盖掉前面的现场。更可靠的做法是:
- 立刻执行
SHOW ENGINE INNODB STATUS\G—— 它只保留最后一次,但内容完整、实时、带上下文 - 长期监控必须配置
innodb_print_all_deadlocks = ON,让每次死锁都落盘;执行SELECT @@innodb_print_all_deadlocks;返回1才算生效 -
information_schema.INNODB_TRX等表只能查“当前”锁等待,死锁发生后瞬间已结束,查不到历史快照
真正容易被忽略的是:trx_query 显示的 SQL 是事务中“最后执行的一条”,而死锁可能由前面某条 SELECT FOR UPDATE 或未提交的 UPDATE 引起;如果事务里有多个 DML,光看最后一句会漏掉真正卡住的那条。











