MySQL 中的 行锁(Record Lock) 和 间隙锁(Gap Lock)

1. 行锁(Record Lock)

定义

  • Record Lock 是 InnoDB 在事务中对索引记录加的锁,用于保护某一行数据不被其他事务修改。
  • 它是基于索引 的锁,如果没有索引,InnoDB 会退化为表锁。

作用

  • 防止其他事务修改或删除当前事务正在处理的行。
  • 保证事务的隔离性(尤其是 REPEATABLE READ 和 SERIALIZABLE 隔离级别)。

触发场景

  • 常见于 SELECT ... FOR UPDATE 或 UPDATE、DELETE 操作。
  • 必须通过索引定位行,否则会锁住更多数据(甚至全表)。

例子

假设有表:

sql 复制代码
CREATE TABLE user ( id INT PRIMARY KEY, name VARCHAR(50), age INT ) ENGINE=InnoDB;

事务 A:

sql 复制代码
BEGIN; SELECT * FROM user WHERE id=5 FOR UPDATE;
  • InnoDB 会在 id=5 这一行的索引记录上加 Record Lock。
  • 事务 B 如果执行:
sql 复制代码
UPDATE user SET age=30 WHERE id=5;

会被阻塞,直到事务 A 提交或回滚。


2. 间隙锁(Gap Lock)

定义

  • Gap Lock 是 InnoDB 在事务中对索引记录之间的间隙加的锁。
  • 它锁住的是索引之间的空隙,而不是具体的行。
  • 主要用于防止幻读(Phantom Read)。

作用

  • 阻止其他事务在某个索引范围内插入新记录。
  • 常见于范围查询(BETWEEN、>、<)或 REPEATABLE READ 隔离级别下的 SELECT ... FOR UPDATE。

触发场景

  • 范围查询并加锁时(FOR UPDATE 或 LOCK IN SHARE MODE)。
  • 在 REPEATABLE READ 下,InnoDB 会自动加 Gap Lock 来防止幻读。

例子

假设表中有数据:

复制代码
id: 1, 5, 10

事务 A:

sql 复制代码
BEGIN; SELECT * FROM user WHERE id BETWEEN 5 AND 10 FOR UPDATE;
  • InnoDB 会对 id=5 和 id=10 加 Record Lock。
  • 同时会对 (5,10) 之间的空隙加 Gap Lock。
  • 事务 B 如果执行:
sql 复制代码
INSERT INTO user VALUES (7, 'Tom', 25);

会被阻塞,因为 (5,10) 的间隙被锁住。


3. 区别总结

锁类型 锁住对象 作用 常见场景
Record Lock 某一行的索引记录 防止该行被修改或删除 精确匹配索引的 UPDATE、DELETE、SELECT ... FOR UPDATE
Gap Lock 索引记录之间的空隙 防止在范围内插入新记录 范围查询加锁,防止幻读

✅ 总结:

  • Record Lock :锁住已有的行,防止修改或删除。
  • Gap Lock :锁住索引间的空隙,防止插入新行。
  • 两者经常一起出现,比如范围查询时会同时锁住已有行和间隙,这种组合锁叫 Next-Key Lock。
sql 复制代码
BEGIN;

SELECT * FROM user WHERE id BETWEEN a AND b FOR UPDATE;

如果a和b距离很大是否直接锁住a和b中间所有行

是的,

在 InnoDB 的 REPEATABLE READ 隔离级别下,执行

sql 复制代码
BEGIN; SELECT * FROM user WHERE id BETWEEN a AND b FOR UPDATE;

会触发 Next-Key Lock (Record Lock + Gap Lock 的组合),它会锁住范围内所有匹配的行 ,并且锁住这些行之间的间隙,防止其他事务在这个范围内插入新数据。


1. 锁定范围的规则

假设 id 是主键索引,表中数据如下:

复制代码
id: 1, 5, 10, 20, 50, 100

如果执行:

sql 复制代码
SELECT * FROM user WHERE id BETWEEN 5 AND 50 FOR UPDATE;

InnoDB 会:

  • 对 id=5、id=10、id=20、id=50 加 Record Lock(行锁)
  • 对 (5,10)、(10,20)、(20,50) 这些间隙加 Gap Lock
  • 还会对 (50, 下一条记录) 的间隙加锁(防止插入 51、60 等)

2. 如果 a 和 b 很远

如果 a=5,b=1000000,而表中有很多行:

  • 是的 ,InnoDB 会锁住 id=5 到 id=1000000 范围内所有匹配的行 和它们之间的间隙。
  • 这意味着这个范围内的更新、删除、插入都会被阻塞,直到事务提交或回滚。
  • 如果范围特别大,可能会造成大范围锁定,严重影响并发性能。

3. 注意事项

  • 基于索引 :行锁和间隙锁都是基于索引的,如果 id 没有索引,InnoDB 会退化成表锁,锁住整张表。
  • 范围越大,锁的行和间隙越多,并发冲突风险越高。
  • 如果只想锁住已存在的行而不锁间隙,可以使用 READ COMMITTED 隔离级别(Gap Lock 不会生效)。

4. 直观示意

假设:

复制代码
id: 1, 5, 10, 20, 50, 100

执行:

sql 复制代码
SELECT * FROM user WHERE id BETWEEN 5 AND 50 FOR UPDATE;

锁定范围:

复制代码
[5]====(gap)====[10]====(gap)====[20]====(gap)====[50]====(gap)====
  • x 表示 Record Lock(行锁)
  • (gap) 表示 Gap Lock(间隙锁)

我建议,如果你的 a 和 b 之间范围特别大,可以考虑:

  • 拆分查询,减少一次性锁定的范围
  • 使用更精确的条件,避免锁住无关数据
  • 降低隔离级别 (如 READ COMMITTED)减少 Gap Lock

为什么5前面没有被锁住?

在执行

sql 复制代码
BEGIN; SELECT * FROM user WHERE id BETWEEN 5 AND 50 FOR UPDATE;

的时候,为什么 id=5 前面的间隙 (比如 (1,5))没有被锁住?


核心原因:锁定范围是由查询条件决定的

InnoDB 的 Next-Key Lock (行锁 + 间隙锁)只会锁住满足查询条件的记录以及这些记录之间的间隙。

  • 查询条件是 id BETWEEN 5 AND 50
  • 范围的起点 是 id=5
  • 所以它不会去锁 (1,5) 这个间隙,因为这个间隙里的值(比如 2、3、4)不满足条件,不会影响当前事务的结果集。

锁定范围的精确规则

在 REPEATABLE READ 隔离级别下:

  1. Record Lock :锁住满足条件的记录(这里是 id=5, 10, 20, 50)。
  2. Gap Lock:锁住这些记录之间的间隙,以及最后一条记录到下一条记录之间的间隙。
  3. 不锁条件范围外的间隙,因为它们不会导致幻读。

例子对比

假设表数据:

复制代码
id: 1, 5, 10, 20, 50, 100

查询:

sql 复制代码
SELECT * FROM user WHERE id BETWEEN 5 AND 50 FOR UPDATE;

锁定范围:

复制代码
[5]====(gap)====[10]====(gap)====[20]====(gap)====[50]====(gap)====
  • [x] = Record Lock
  • (gap) = Gap Lock
  • (1,5) 不锁,因为不在条件范围内。

如果是:

sql 复制代码
SELECT * FROM user WHERE id >= 5 FOR UPDATE;

锁定范围:

复制代码

复制代码

[5]====(gap)====[10]====(gap)====[20]====(gap)====[50]====(gap)====[100]====(gap)====

  • 依然不会锁 (1,5),因为条件是 >=5。

如果是:

sql 复制代码
SELECT * FROM user WHERE id > 1 FOR UPDATE;

锁定范围:

复制代码
(1,5)====[5]====(gap)====[10]====(gap)====[20]====(gap)====[50]====(gap)====[100]====(gap)====
  • 这时 (1,5) 会被锁,因为它在条件范围内(>1)。

✅ 总结:

  • InnoDB 只锁条件范围内的记录和间隙,不会锁条件范围外的间隙。
  • BETWEEN 5 AND 50 的起点是 5,所以 5 前面的间隙不会被锁。
  • 如果条件包含更小的值(比如 >1),那么 5 前面的间隙就会被锁。
相关推荐
一只鹿鹿鹿3 小时前
政务外网整体设计方案(PPT)
大数据·数据库·web安全·系统安全·流程图·政务
哦虎!3 小时前
【JavaEE】网络初识
java
2603_965148113 小时前
社区与学习资源:从入门到精通的API选品学习路径
数据库·人工智能·python·学习·数据挖掘
for_ever_love__3 小时前
MySQL ORDER BY 排序优化:从 Using filesort 到索引有序扫描(含双路 / 单路排序原理)
java·mysql·优化·存储·by·order·filesort
AI砖家3 小时前
安全进入 Agent Loop:2027 年 Coding Agent 的决胜点
数据库·安全·codex·vibecoding
wangqiaowq4 小时前
SQL server C盘空间占满的处理
数据库·sql
文人sec4 小时前
SQL基础入门笔记 | 从零基础到掌握核心查询语句
数据库·笔记·sql
数字新视界4 小时前
机房动环监控提升运维效率
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
我是人✓4 小时前
Java文件上传三方式对比:从传统到SpringBoot
java·开发语言
SelectDB技术团队4 小时前
冷热分层从建到验:Storage Policy 创建、表级与分区级绑定、SHOW TABLETS 验证的完整命令
大数据·数据库·elasticsearch·搜索引擎·数据分析·apache doris·冷热分层