高 困难
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\GsqlLATEST 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 仍可能