INNODB_LOCK_WAITS不可靠,应优先用INNODB_TRX找LOCK WAIT事务,再结合INNODB_LOCKS和SHOW ENGINE INNODB STATUS定位真实阻塞者;GAP锁在RC下仍存在,需依lock_space/lock_page/heapno交叉比对确认冲突。

别只信 INNODB_LOCK_WAITS 的等待链
MySQL 5.7 中 INNODB_LOCK_WAITS 表字段精简、关联不稳定,常返回空或指向已结束事务,blocking_trx_id 字段可能为空或失效。它不记录 GAP 锁等待,对 INSERT ... ON DUPLICATE KEY UPDATE 场景下的插入意向锁冲突完全不可见。
实操建议:
- 先查
INFORMATION_SCHEMA.INNODB_TRX找出trx_state = 'LOCK WAIT'的事务,并按trx_started ASC排序——越早开始的越可能是“长睡未提交”的元凶 - 再用
waiting_trx_id去INNODB_LOCKS查它等的是哪把锁(注意看lock_space/lock_page/lock_rec三元组) - 最后回
SHOW ENGINE INNODB STATUS\G的TRANSACTIONS段落,手动匹配持有该锁位置的trx_id,这才是真实阻塞者
看到 gap before rec 别急着调隔离级别
死锁日志里出现 lock_mode X locks gap before rec insert intention 或 S locks gap before rec,不代表业务逻辑主动加了间隙锁,而是 InnoDB 在唯一索引上做 duplicate-key 检查时强制行为——即使在 READ-COMMITTED 隔离级别下也存在。
常见误判点:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 以为开了 RC 就没 GAP 锁 → 实际上唯一键检查、外键约束仍会用 GAP 锁
- 把
insert intention当成普通 GAP 锁 → 它和 RECORD X 锁互斥,但和其它插入意向锁兼容 - 忽略
HOLDS S lock on gap是 INSERT … ON DUPLICATE KEY UPDATE 的检查锁,不是你写的 SELECT FOR UPDATE 加的
从死锁日志定位物理锁位必须交叉比对
死锁是否真由 GAP 冲突引发,不能单看锁类型描述。关键判断依据是两个事务是否在**同一索引、同一页面、相邻或重叠的记录位置**上加了互斥锁。
操作步骤:
- 从死锁日志中提取两个事务等待的
lock_index(如songId_idx)和heapno(如heapno=3) - 查
INNODB_LOCKS中相同lock_index+ 相同lock_space/lock_page的记录 - 若事务 A 持有
lock_mode = X+lock_type = RECORD,事务 B 等待lock_mode = X+lock_type = INSERT_INTENTION,且lock_page相同但lock_rec不同 → 这就是典型的 GAP + 插入意向锁循环等待
SHOW ENGINE INNODB STATUS 是唯一权威快照源
它提供实时、完整、带上下文的锁视图,LATEST DETECTED DEADLOCK 段落包含事务状态、SQL、锁结构数、堆记录号等不可替代信息;而 INNODB_TRX/INNODB_LOCKS 是采样快照,可能错过瞬态锁。
容易被忽略的细节:
- 日志中
*** (1) TRANSACTION:后面的MySQL thread id可直接用于KILL,比查表更快 -
heapno是页内偏移,不是主键值;lock_rec是物理记录地址,需结合lock_page和lock_space才能定位到具体间隙 - 若日志末尾出现
TOO DEEP OR LONG SEARCH IN THE LOCK TABLE WAITS-FOR GRAPH,说明等待链超限(>200 事务或 >1M 锁),此时死锁检测已退化为超时回滚,不是常规 GAP 冲突

















