MySQL 锁与死锁:行锁、间隙锁、Next-Key Lock 与排查手册

事务讲到最后,面试官往往会把你从"隔离级别"拉到"线上事故":

  • 为什么卡住?
  • 为什么死锁?
  • 为什么 RR 下会出现间隙锁?

你必须记住的 3 句话(面试直出):

  • InnoDB 的锁本质是:为保证语义,在索引上加锁,不是在"表/行"这个抽象上加锁。
  • RR 下为避免范围内插入导致语义破坏,会出现 Next-Key Lock(记录锁 + 间隙锁)
  • 死锁不是"代码写错了"这么简单,更多是 锁顺序不一致 + 范围条件/索引缺失扩大锁范围

1. 先建立锁的主线:锁的是"索引范围"

常见锁类型(面试够用版):

  • 记录锁(Record Lock):锁住某条索引记录
  • 间隙锁(Gap Lock):锁住索引记录之间的间隙,防止插入
  • Next-Key Lock:记录锁 + 间隙锁,锁住一个"半开区间"的范围

关键结论:

  • 是否走索引,决定锁的粒度
  • 没有合适索引,可能导致更大的范围被锁(甚至看起来像"锁表")。

2. 为什么会有间隙锁:是为了保证 RR 的范围语义

你可以用这个典型场景解释:

  • 事务 A:SELECT ... FOR UPDATE WHERE age BETWEEN 10 AND 20
  • 如果不锁间隙,事务 B 可以插入一条 age=15
  • A 再次执行范围更新/校验时,语义就被破坏(出现"范围内新增")

因此 RR 下,当前读/范围修改可能引入:

  • 间隙锁、Next-Key Lock

更精确的工程口径(避免被追问时说含糊):

  • 间隙锁主要出现在 RR + 当前读/修改 + 范围扫描 这些组合里
  • 如果能被 唯一索引等值命中,很多场景会退化成记录锁(锁范围更小)
  • 一旦是 非唯一索引范围条件索引缺失导致扫描,锁范围就容易膨胀

面试加分表达:

  • 间隙锁不是为了"防幻读这个词",而是为了"防止范围内出现新记录导致语义不一致"

3. 死锁怎么来的:用"锁顺序"解释最稳

死锁的经典结构:

  • T1 持有 A,等待 B
  • T2 持有 B,等待 A

在 InnoDB 里常见触发点:

  • 两个事务更新同一批行,但 更新顺序不同
  • 范围条件导致锁集合不一致(或扩大锁范围)
  • 二级索引更新会涉及主键索引回表锁定(锁链更复杂)

一个工程上最常见的死锁模型:

  • 事务 1:先更新 id=1 再更新 id=2
  • 事务 2:先更新 id=2 再更新 id=1

解决思路通常不是"加大超时",而是:

  • 固定更新顺序(按主键升序)
  • 缩小事务
  • 确保条件走索引,减少锁范围

再补一组更"线上可落地"的降死锁动作(你可以按优先级讲):

  • 固定访问顺序:同一批资源按主键/业务 key 排序后依次更新
  • 把"先查再改"改为"条件更新" :把校验写进 UPDATE ... WHERE ...,减少锁持有时间与往返
  • 避免范围大批量更新:拆批、按主键分页、并控制并发
  • 补齐索引:让条件尽量走到"更小范围"的索引记录,减少 Next-Key 锁范围
  • 只做兜底的重试:对死锁错误做幂等重试,但不把重试当根治

4. 最容易踩坑的 4 个点

  • 坑 1:SELECT ... FOR UPDATE 以为只锁一行

    • 走范围条件/非唯一索引时可能锁范围(Next-Key)。
  • 坑 2:缺失索引导致锁范围扩大

    • 条件无法定位到小范围索引记录,锁住大片区间。
  • 坑 3:在事务里做慢操作

    • 事务越长,持锁越久,冲突越明显。
  • 坑 4:批量更新/分页更新的锁冲突

    • 大事务 + 范围扫描容易放大锁等待与死锁概率。

5. 线上排查手册:锁等待与死锁

5.1 锁等待(接口变慢/超时)

你要快速回答三个问题:

  • 谁在等?
  • 在等谁?
  • 等的锁是什么范围?

常用手段:

  • SHOW PROCESSLIST:看到 Waiting for ... lock
  • SHOW ENGINE INNODB STATUS:看 TRANSACTIONSLATEST DETECTED DEADLOCK
  • performance_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. 自测清单(你要能顺口讲出来)

  • Q:InnoDB 的锁锁在哪里?

    • A:锁在索引上;是否走索引决定锁粒度与范围。
  • Q:Next-Key Lock 是什么?

    • A:记录锁 + 间隙锁,用于 RR 下的范围当前读/修改,保证范围语义。
  • Q:死锁怎么降低概率?

    • A:固定锁顺序、缩短事务、让条件走索引缩小锁范围,必要时做幂等重试。

7. 30 秒背诵稿

InnoDB 为保证事务语义会在索引上加锁,常见有记录锁、间隙锁和 Next-Key Lock。RR 下范围当前读/修改为了防止范围内插入破坏语义,可能加 Next-Key 锁导致锁范围变大。死锁通常来自锁顺序不一致或索引缺失导致锁集合扩大,线上先用 processlist/innodb status/performance_schema 找到阻塞链和死锁双方,再通过固定更新顺序、缩短事务、补索引来根治。

相关推荐
星蓝_starblue23 分钟前
零服务器、零数据库!开源growth-board,利用GitHub自动管理刷题/学习/求职全流程
服务器·数据库·程序人生·系统架构·node.js·github·改行学it
三8443 小时前
sqli-labs1-10通关笔记
数据库
BullSmall4 小时前
Another Redis Desktop Manager(ARDM)下载指南
数据库·redis·缓存
SelectDB4 小时前
Apache Doris 倒排索引工作原理:全文检索提速 59 倍,点查提速 14 倍
数据库
码农颜4 小时前
5.1.1 原⼦性
数据库·mysql
机建狂魔4 小时前
Codex 接入第三方模型 API 实战:以 Mimo 为例
java·服务器·数据库·ai·ai编程·codex
山峰哥5 小时前
数据库工程与SQL调优:从慢查询到秒级响应的实战之路
java·开发语言·数据库·sql·深度优先·启发式算法
moMo5 小时前
# 向量数据库入门:从"关键词匹配"到"语义理解"
数据库
ACP广源盛139246256735 小时前
DeepSeek‑V4‑Flash 公测@ACP#昇腾 950 国产算力组合落地,国产 PCIe 交换芯片 IX9104 有哪些硬件机会
大数据·数据库·人工智能·分布式·单片机·嵌入式硬件·microsoft
进击的女IT6 小时前
通过mysql中的Data目录恢复数据库数据
数据库·mysql