最直接查看最近一次死锁详情的方式是执行SHOW ENGINE INNODB STATUS\G,重点解析LATEST DETECTED DEADLOCK区块中的事务ID、HOLDS/WAITING锁信息、末尾SQL及锁类型(X/S/GAP),精准定位循环等待根源。

死锁不是数据库故障,而是并发逻辑缺陷的信号——只要事务加锁顺序不一致、索引缺失或间隙锁范围失控,就一定会复现。MySQL 8.0 的 innodb_print_all_deadlocks = ON 和 performance_schema.data_locks 已足够定位根因,关键在能否从日志里读出「谁锁了什么、为什么锁、为什么等不到」。
怎么看最近一次死锁详情(SHOW ENGINE INNODB STATUS)
这是最直接的入口,但很多人只扫一眼就放弃。真正有用的线索藏在 LATEST DETECTED DEADLOCK 区块里:
- 先找被回滚的事务(
ROLLING BACK行),它通常是持有锁最少的那个,不代表它是“问题方” - 对比两个事务的
mysql thread id和trx_id,再结合information_schema.INNODB_TRX查它们的启动时间、SQL、事务状态 - 重点看每条 SQL 后面的
lock_mode X locks rec but not gap或lock_mode X locks gap before rec——前者是精准行锁,后者是间隙锁,后者更容易引发交叉冲突 - 注意
lock struct(s)下的lock_trx_id和lock_rec_lock,能确认锁住的具体索引和记录值(比如PRIMARY, 123或idx_name, 'Eason')
怎么查当前锁等待链(sys.innodb_lock_waits)
当死锁已发生但还没被检测到(比如刚卡住几秒),或者想确认是否正在形成循环等待,这个视图比手写 JOIN 更可靠:
-
waiting_trx_id和blocking_trx_id是核心,直接对应两个事务 ID -
waiting_pid和blocking_pid是线程 ID,可立刻用KILL终止阻塞源(慎用,优先查清业务逻辑) -
waiting_query显示卡住的 SQL,常含FOR UPDATE或UPDATE ... WHERE;blocking_query往往是更早执行、尚未提交的语句 - 如果返回空,不代表没锁——可能只是没形成等待链,需进一步查
performance_schema.data_locks
怎么验证是不是索引失效导致锁扩大(EXPLAIN + data_locks)
90% 的死锁根源不在并发本身,而在一条本该走索引却全表扫描的 SQL。它会让 UPDATE 锁住成百上千行,大幅提高冲突概率:
- 对报错 SQL 执行
EXPLAIN,重点看type是否为ALL或index,key是否为NULL,rows是否远超预期 - 再查
performance_schema.data_locks,过滤LOCK_TYPE = 'RECORD'且LOCK_DATA大量重复或跨度极大(如从 1 到 5000) - 常见诱因:
WHERE条件用了函数(DATE(create_time))、隐式类型转换(phone = 13800138000)、OR中混入非索引列、联合索引未满足最左前缀 - 修复后务必验证:改完索引或 SQL,用相同数据量压测,并观察
data_locks中锁行数是否收敛到个位数
怎么避免间隙锁引发的死锁(REPEATABLE READ 下的 GAP 锁)
MySQL 8.0 默认隔离级别下,WHERE 条件命中不到记录时,InnoDB 会加间隙锁防幻读——这在高并发插入场景极易触发死锁,尤其当多个事务试图向同一间隙插入不同值:
- 典型现象:两条
INSERT ... SELECT或INSERT ... ON DUPLICATE KEY UPDATE并发执行,日志显示lock_mode X locks gap before rec - 临时缓解:把隔离级别降为
READ COMMITTED(SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED),间隙锁失效,但需评估幻读风险 - 根本解法:用唯一约束+重试代替“先查后插”,或改用
SELECT ... FOR UPDATE显式锁定间隙(需精确控制范围) - 警惕
ORDER BY ... LIMIT配合FOR UPDATE:即使只取 1 行,也可能锁住后续所有满足条件的间隙
真正难的不是发现死锁,而是判断哪一行 SQL 在哪个事务里、以什么顺序、锁住了哪些索引项。所有工具都只是镜子,照出的是业务代码里的并发假设——比如“这个 ID 一定存在”“这两张表更新顺序无关紧要”“用户不会同时点两次提交”。这些假设一旦在高并发下被打破,死锁就是必然结果。


















