孤舟笔记 并发篇十一 行锁、间隙锁、临键锁傻傻分不清?MySQL InnoDB的锁其实就这三板斧

文章目录

个人网站

面试被问到"MySQL 有哪些锁",你脱口而出"行锁、表锁"。面试官追问"间隙锁呢?临键锁呢?",你瞬间懵了。更头疼的是,这三个锁的关系像俄罗斯套娃,一个套一个,到底谁包含谁?

今天咱们就把 InnoDB 这三种行级锁彻底理清,让你面试时能画出来、讲明白。

先说结论:三种锁的核心要点

维度 说明
行锁(Record Lock) 锁定索引中的一条记录(行)
间隙锁(Gap Lock) 锁定索引记录之间的间隙,防止插入
临键锁(Next-Key Lock) 行锁 + 间隙锁,左开右闭区间
关系 临键锁 = 间隙锁 + 行锁
行锁锁定对象 索引记录,不是数据行
间隙锁目的 防止幻读,不允许往间隙里插入新记录
临键锁默认 InnoDB 默认加锁方式

一句话记住:行锁锁一条,间隙锁锁一段空隙,临键锁是它俩合体------锁一条+锁一段。

行锁:精确打击,锁住一条记录

行锁是最简单直接的一种锁------锁定索引中的某一条记录

sql 复制代码
SELECT * FROM user WHERE id = 5 FOR UPDATE;  -- 锁住 id=5 这一行 👈

别人可以读,但不能修改 id=5 这行,也不能用 FOR UPDATELOCK IN SHARE MODE 再锁它。

生活类比: 行锁就像图书馆里你正在看的那本书------别人可以透过玻璃橱窗看到(普通读),但不能拿走或涂改。

注意一个坑: 行锁锁的是索引,不是数据行。如果你的查询没走索引(比如用非索引字段更新),InnoDB 退化为表锁,所有行都被锁住!这就是为什么面试总强调"一定要走索引"。

间隙锁:锁住"空地",防止插队

间隙锁是 InnoDB 独有的,专门为了解决幻读问题。

假设你的表里 id 有 5、10、15 三条记录。间隙就是 (5, 10) 和 (10, 15) 以及 (-∞, 5) 和 (15, +∞) 这四段"空地"。

sql 复制代码
SELECT * FROM user WHERE id > 5 AND id < 10 FOR UPDATE;
-- 间隙锁锁住 (5, 10),不允许往这个区间插入新记录 👈

别人不能往 (5, 10) 之间插入 id=6、7、8、9 的记录。 但可以修改 id=5 或 id=10 本身------因为那不是间隙,那是行。

生活类比: 间隙锁就像电影院选座------你选了 5 排和 10 排,中间的空座虽然没人坐,但不允许别人再选了,防止"插队"出现新的人。

间隙锁的特殊之处: 间隙锁之间不互斥。两个事务可以同时持有同一个间隙的间隙锁------因为间隙锁只防插入,不防读取。

临键锁:行锁 + 间隙锁的合体

临键锁(Next-Key Lock)是 InnoDB 默认的加锁方式,它是行锁和间隙锁的组合:

复制代码
Next-Key Lock = Gap Lock + Record Lock

锁住的是一个左开右闭区间。比如 id 有 5、10、15,那么临键锁锁住的区间是:

  • (-∞, 5]
  • (5, 10]
  • (10, 15]
  • (15, +∞)
sql 复制代码
SELECT * FROM user WHERE id = 10 FOR UPDATE;
-- 临键锁锁住 (5, 10] 区间 👈
-- 间隙部分 (5, 10):不能插入 id=6、7、8、9
-- 行锁部分 [10]:不能修改 id=10

为什么是左开右闭? 因为 InnoDB 按 B+ 树索引顺序扫描,遇到 10 时,要把前面的间隙也锁住,防止有人在 5 和 10 之间插入新记录导致幻读。

生活类比: 临键锁就像你占了一个座位,不仅占了这个座,还把前面到上一个已占座位之间的空座都占了------别人既不能坐你的座(行锁),也不能坐中间的空座(间隙锁)。

退化规则:什么时候锁会"缩小"

临键锁不是永远锁整个区间,InnoDB 有优化机制:

  1. 等值查询,命中记录 → 临键锁退化为行锁(只锁命中行)
  2. 等值查询,未命中记录 → 临键锁退化为间隙锁(只锁间隙)
  3. 范围查询 → 保持临键锁,不会退化
sql 复制代码
-- 情况1:id=10 存在 → 退化为行锁,只锁 id=10
SELECT * FROM user WHERE id = 10 FOR UPDATE;

-- 情况2:id=7 不存在 → 退化为间隙锁,锁 (5, 10)
SELECT * FROM user WHERE id = 7 FOR UPDATE;

-- 情况3:范围查询 → 临键锁,锁 (5, 10] + (10, 15]
SELECT * FROM user WHERE id > 5 AND id <= 15 FOR UPDATE;

理解退化规则很重要,面试时能区分这三种情况,说明你真的懂了。

InnoDB 行级锁全景

复制代码
InnoDB 行级锁 全景

三种锁
├── 行锁(Record Lock)── 锁定一条索引记录
├── 间隙锁(Gap Lock)── 锁定索引间隙,防插入
└── 临键锁(Next-Key Lock)── 行锁 + 间隙锁
    └── 左开右闭区间 (a, b]

关系
临键锁 = 间隙锁 + 行锁

退化规则
├── 等值查询 + 命中 → 退化为行锁
├── 等值查询 + 未命中 → 退化为间隙锁
└── 范围查询 → 保持临键锁

口诀:行锁锁一条,间隙防插队,
      临键是合体,退化看命中。

回答技巧与点评

标准回答

InnoDB 有三种行级锁:行锁锁定索引中的一条记录;间隙锁锁定索引记录之间的间隙,防止其他事务插入新记录;临键锁是行锁和间隙锁的组合,锁定左开右闭区间。临键锁是 InnoDB 默认的加锁方式。等值查询命中记录时临键锁退化为行锁,未命中时退化为间隙锁,范围查询保持临键锁。

加分回答
  1. 设计哲学:间隙锁和临键锁是 InnoDB 在可重复读(RR)隔离级别下解决幻读问题的核心机制。MVCC 解决了普通读的幻读,而临键锁解决了当前读的幻读,两者配合实现完整的 RR 隔离
  2. 边界情况:行锁锁的是索引不是数据行,不走索引的查询会退化为表锁;间隙锁之间不互斥,但间隙锁和插入操作互斥;在读已提交(RC)隔离级别下,间隙锁基本不生效
  3. 实际应用 :在线交易系统中,用 SELECT ... FOR UPDATE 锁定账户行防止并发修改;用临键锁防止范围内新增记录导致的幻读。理解加锁范围对排查死锁至关重要
面试官点评

这道题考的是你对 InnoDB 锁机制的体系化理解。只会背三种锁的名字只能拿基础分。能说清临键锁的组成关系、退化规则、以及和幻读的关系,才能拿高分。如果你能提到不同隔离级别下锁的行为差异,面试官会认为你有实战深度。

原文阅读


内容有帮助?点赞、收藏、关注三连!评论区等你 💪

相关推荐
zhoupenghui1683 小时前
Mysql插入数据时,怎么让自增的主键续接表当前最大ID+1
数据库·mysql·auto increment
song8546011343 小时前
MYSQL优化器的主要的优化策略及其示例
数据库·mysql
Bert.Cai3 小时前
MySQL RAND()函数详解
数据库·mysql
与数据交流的路上4 小时前
mysql参数-优化器 range_optimizer_max_mem_size 相关
数据库·mysql
喝可乐的希饭a4 小时前
MYSQL的mvcc
数据库·mysql
Bert.Cai4 小时前
MySQL MOD()函数详解
数据库·mysql
William Dawson4 小时前
【MySQL触发器超详细实战教程|从零基础到项目生产可用(避坑+案例+跨库+逗号拆分)】
数据库·mysql
czlczl200209254 小时前
MySQL 中为什么我们要避免“多个范围查询”
数据库·mysql
HackTorjan4 小时前
MySQL高可用架构设计与最佳实践
android·人工智能·mysql·adb·自动化