mysql 5.7中死锁必留痕迹,需通过show engine innodb status查latest detected deadlock定位持锁与等待关系,结合innodb_trx筛选lock wait和sleep事务,再用innodb_locks交叉比对lock_space/lock_page/lock_rec确认gap锁物理重叠,不可依赖innodb_lock_waits。

MySQL 5.7 中死锁不是“查不到就没了”,而是必然留下痕迹——关键是你得去对的地方、用对的方式抓取,否则容易在 INNODB_LOCK_WAITS 里空转,或误判 SHOW ENGINE INNODB STATUS 里的“gap before rec”为配置错误。
直接看 SHOW ENGINE INNODB STATUS\G 的 TRANSACTIONS 段落
这是最快定位当前死锁现场的入口,不依赖任何表查询,也不受 INNODB_LOCK_WAITS 字段缺失影响。
- 执行后搜
LATEST DETECTED DEADLOCK,它只保留最近一次死锁快照,但足够用于复盘 - 重点关注两块:
Locks held(谁持有什么锁)、Locks waiting(谁在等什么锁),注意每个事务末尾的*** WE ROLL BACK TRANSACTION行,它明确告诉你 MySQL 主动回滚了哪个事务(通常是 undo log 更小的那个) - 若没看到死锁日志,说明还没触发自动检测,但可能已存在长期 LOCK WAIT —— 此时搜
TRANSACTIONS段落里所有状态为LOCK WAIT的事务,它们就是正在卡住的活线索
用 INFORMATION_SCHEMA.INNODB_TRX 找出隐性持锁者
很多卡死不是因为“正在执行 UPDATE”,而是因为一个早已休眠却没提交的事务一直霸着锁。
- 先跑:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_state = 'LOCK WAIT' ORDER BY trx_started ASC—— 等得越久的事务,越可能是被前面某个“Sleep”事务拖住的 - 再补查:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_state = 'RUNNING' AND COMMAND = 'Sleep'—— 这类记录危险系数最高,尤其trx_started时间远早于当前时间,基本可断定是连接池归还连接前漏了COMMIT或ROLLBACK - 配合
INFORMATION_SCHEMA.PROCESSLIST查对应ID的State和Info字段,确认它是否真无活跃语句;若Info为空、State是Sleep,且Time很大,KILL 前优先试KILL QUERY [ID]
交叉比对 INNODB_LOCKS 定位 GAP 锁冲突点
当死锁日志里出现 lock_mode X locks gap before rec insert intention,别急着调隔离级别——这在 RC 下对唯一索引做 duplicate-key 检查时本就合法,真正要确认的是:两个事务锁的物理位置是否重叠。
- 从死锁日志提取关键信息:涉及的索引名(如
songId_idx)、lock_space/lock_page(页号)、lock_rec(记录堆号) - 查
INNODB_LOCKS:SELECT lock_trx_id, lock_mode, lock_type, lock_index, lock_space, lock_page, lock_rec FROM INFORMATION_SCHEMA.INNODB_LOCKS WHERE lock_index = 'songId_idx' AND lock_space = XXX AND lock_page = YYY - 若事务 A 持有
lock_mode = S+lock_type = RECORD,事务 B 等待lock_mode = X+ 同一lock_rec→ 是行锁冲突;若 A 持有X + RECORD,B 等待X + INSERT_INTENTION且lock_page相同但lock_rec不同 → 典型 GAP + 插入意向锁循环等待
别信 INNODB_LOCK_WAITS 能直接给出阻塞链
MySQL 5.7 的 INNODB_LOCK_WAITS 字段已被精简,blocking_trx_id 经常为空或指向已结束事务,无法反映 GAP 锁等待,也不能覆盖所有阻塞场景。
- 它只映射 RECORD 级别的直接阻塞,对间隙锁、插入意向锁组成的间接等待链完全不记录
- 常见陷阱:执行关联查询后返回空结果,不代表没阻塞,只是这张表没存下完整链路
- 替代做法:用
INNODB_TRX找到 waiting_trx_id → 手动查INNODB_LOCKS得到它等待的lock_space/lock_page/lock_rec→ 再查哪些其他事务在相同位置持有兼容性冲突的锁(比如一个持 S-record,另一个等 X-gap)
最易被忽略的一点:死锁日志里写的“HOLDS S lock on gap”,往往不是业务 SQL 显式加的,而是 INSERT ... ON DUPLICATE KEY UPDATE 在唯一索引上做重复键检查时 InnoDB 强制加的——它不体现为你的代码逻辑,但会实实在在参与锁竞争。排查时必须把 SQL 语义、索引结构、隔离级别三者叠在一起看,而不是单看某一行日志就下结论。











