MySQL 锁与死锁:行锁、间隙锁、Next-Key Lock 与排查手册
事务讲到最后面试官往往会把你从“隔离级别”拉到“线上事故”为什么卡住为什么死锁为什么 RR 下会出现间隙锁你必须记住的 3 句话面试直出InnoDB 的锁本质是为保证语义在索引上加锁不是在“表/行”这个抽象上加锁。RR 下为避免范围内插入导致语义破坏会出现Next-Key Lock记录锁 间隙锁。死锁不是“代码写错了”这么简单更多是锁顺序不一致 范围条件/索引缺失扩大锁范围。1. 先建立锁的主线锁的是“索引范围”常见锁类型面试够用版记录锁Record Lock锁住某条索引记录间隙锁Gap Lock锁住索引记录之间的间隙防止插入Next-Key Lock记录锁 间隙锁锁住一个“半开区间”的范围关键结论是否走索引决定锁的粒度。没有合适索引可能导致更大的范围被锁甚至看起来像“锁表”。2. 为什么会有间隙锁是为了保证 RR 的范围语义你可以用这个典型场景解释事务 ASELECT ... FOR UPDATE WHERE age BETWEEN 10 AND 20如果不锁间隙事务 B 可以插入一条age15A 再次执行范围更新/校验时语义就被破坏出现“范围内新增”因此 RR 下当前读/范围修改可能引入间隙锁、Next-Key Lock更精确的工程口径避免被追问时说含糊间隙锁主要出现在RR 当前读/修改 范围扫描这些组合里如果能被唯一索引等值命中很多场景会退化成记录锁锁范围更小一旦是非唯一索引、范围条件、索引缺失导致扫描锁范围就容易膨胀面试加分表达间隙锁不是为了“防幻读这个词”而是为了“防止范围内出现新记录导致语义不一致”。3. 死锁怎么来的用“锁顺序”解释最稳死锁的经典结构T1 持有 A等待 BT2 持有 B等待 A在 InnoDB 里常见触发点两个事务更新同一批行但更新顺序不同范围条件导致锁集合不一致或扩大锁范围二级索引更新会涉及主键索引回表锁定锁链更复杂一个工程上最常见的死锁模型事务 1先更新id1再更新id2事务 2先更新id2再更新id1解决思路通常不是“加大超时”而是固定更新顺序按主键升序缩小事务确保条件走索引减少锁范围再补一组更“线上可落地”的降死锁动作你可以按优先级讲固定访问顺序同一批资源按主键/业务 key 排序后依次更新把“先查再改”改为“条件更新”把校验写进UPDATE ... WHERE ...减少锁持有时间与往返避免范围大批量更新拆批、按主键分页、并控制并发补齐索引让条件尽量走到“更小范围”的索引记录减少 Next-Key 锁范围只做兜底的重试对死锁错误做幂等重试但不把重试当根治4. 最容易踩坑的 4 个点坑 1SELECT ... FOR UPDATE以为只锁一行走范围条件/非唯一索引时可能锁范围Next-Key。坑 2缺失索引导致锁范围扩大条件无法定位到小范围索引记录锁住大片区间。坑 3在事务里做慢操作事务越长持锁越久冲突越明显。坑 4批量更新/分页更新的锁冲突大事务 范围扫描容易放大锁等待与死锁概率。5. 线上排查手册锁等待与死锁5.1 锁等待接口变慢/超时你要快速回答三个问题谁在等在等谁等的锁是什么范围常用手段SHOW PROCESSLIST看到Waiting for ... lockSHOW ENGINE INNODB STATUS看TRANSACTIONS与LATEST DETECTED DEADLOCKperformance_schema/sys库视图定位阻塞链落地判断如果阻塞链上“持锁者”的 SQL 很慢/事务很长先把它当“长事务/慢 SQL”处理。5.2 死锁InnoDB 会主动回滚一方现象应用报错Deadlock found when trying to get lock; try restarting transaction处理姿势第一时间取SHOW ENGINE INNODB STATUS死锁信息会被覆盖别拖看清楚哪两个事务各自持有什么锁各自等待什么锁你在 status 里需要重点抓的“可还原信息”面试/实战都加分两边事务的 SQL到底是哪条语句触发锁的是哪个索引主键/二级索引是等值还是范围决定是否可能是 Next-Key两个事务的执行顺序是否锁顺序相反工程修复优先级固定锁顺序按主键排序更新缩小事务与批次补索引/改查询条件缩小锁范围业务上允许时做幂等重试只作为兜底不是根治6. 自测清单你要能顺口讲出来QInnoDB 的锁锁在哪里A锁在索引上是否走索引决定锁粒度与范围。QNext-Key Lock 是什么A记录锁 间隙锁用于 RR 下的范围当前读/修改保证范围语义。Q死锁怎么降低概率A固定锁顺序、缩短事务、让条件走索引缩小锁范围必要时做幂等重试。7. 30 秒背诵稿InnoDB 为保证事务语义会在索引上加锁常见有记录锁、间隙锁和 Next-Key Lock。RR 下范围当前读/修改为了防止范围内插入破坏语义可能加 Next-Key 锁导致锁范围变大。死锁通常来自锁顺序不一致或索引缺失导致锁集合扩大线上先用 processlist/innodb status/performance_schema 找到阻塞链和死锁双方再通过固定更新顺序、缩短事务、补索引来根治。