面试知识库
困难

MySQL死锁排查#

一句话答案#

死锁是两个事务互相持有对方需要的锁导致循环等待,InnoDB 自动检测并回滚代价小的事务;排查用 SHOW ENGINE INNODB STATUS 看死锁日志,定位具体 SQL 和锁类型。

核心要点

典型死锁场景#

场景 1:交叉更新#

-- 事务 A                       -- 事务 B
UPDATE accounts SET ...          
  WHERE id=1;  -- 锁 id=1       UPDATE accounts SET ...
                                   WHERE id=2;  -- 锁 id=2
UPDATE accounts SET ...          
  WHERE id=2;  -- 等 id=2        UPDATE accounts SET ...
                                   WHERE id=1;  -- 等 id=1 → 死锁
sql

场景 2:间隙锁死锁(RR 隔离级别)#

两个事务各自的间隙锁阻塞对方的 INSERT,在 RR 下高发。

排查:SHOW ENGINE INNODB STATUS#

SHOW ENGINE INNODB STATUS\G
sql

LATEST DETECTED DEADLOCK 段关键信息:

  • *** (1) HOLDS THE LOCK(S) — 事务 1 持有的锁
  • *** (1) WAITING FOR THIS LOCK TO BE GRANTED — 事务 1 等待的锁
  • *** (2) HOLDS / WAITING — 事务 2 同上
  • *** WE ROLL BACK TRANSACTION (2) — InnoDB 选择回滚的事务

InnoDB 死锁检测#

  • 使用**等待图(wait-for graph)**检测,发现环路即死锁
  • 回滚代价最小的事务(undo log 量最少)
  • 检测是即时的(每次加锁等待时触发)
  • innodb_deadlock_detect=ON(默认开启)

预防策略#

策略原理
固定加锁顺序所有事务按相同顺序访问资源(如按 id 升序)
缩短事务减少锁持有时间
降低隔离级别RR → RC 消除间隙锁
走好索引避免索引失效导致锁升级为表锁

监控#

SET GLOBAL innodb_print_all_deadlocks = ON;  -- 死锁记录到 error log
SELECT * FROM performance_schema.data_lock_waits;  -- MySQL 8.0 实时锁等待
sql
面试回答(2分钟版)

死锁是两个事务互相持有对方需要的锁形成循环等待。InnoDB 通过等待图算法实时检测死锁,发现环路后自动回滚 undo log 代价最小的那个事务。排查的核心手段是 SHOW ENGINE INNODB STATUS,找到 LATEST DETECTED DEADLOCK 段,看两个事务各自 HOLDS 和 WAITING 的锁,就能理清死锁链。在 RR 隔离级别下间隙锁是死锁高发区,两个事务各自的间隙锁阻塞对方的 INSERT 非常常见。预防策略我总结为四点:所有事务按固定顺序访问资源、缩短事务减少锁持有时间、考虑降到 RC 消除间隙锁、确保查询走好索引避免锁升级为表锁。线上我还会开启 innodb_print_all_deadlocks 把死锁记录到 error log 方便事后分析。

追问与易错

追问方向:

  • “InnoDB 怎么检测死锁?”→ 等待图算法,发现环路即死锁
  • “怎么预防间隙锁导致的死锁?”→ 降到 RC / 减少范围查询 + INSERT 的组合

易错点:

  • ❌ “死锁会导致两个事务都卡住”——InnoDB 自动检测并回滚一个
  • ❌ “间隙锁只在 Serializable 下才有”——RR 就有间隙锁
  • ❌ “用乐观锁就没有死锁”——SELECT … FOR UPDATE 仍可能