MySQL 啥时候用记录锁,啥时候用间隙锁?

article/2025/9/15 14:27:51

 MySQL 啥时候会用记录锁,啥时候会用间隙锁,啥时候又会用 Next-Key 锁呢?今天我们就来做一些测试,弄清楚这个问题。

文章思维导图

影响因素

在开始之前,我们需要声明的是:本文所有测试及结论的前提均是在「可重复读」隔离级别下,以及 Innodb 存储疫情下。

根据网上资料,我们大概可以知道,影响其使用哪种行级锁的因素有:

  1. 索引类型(聚簇索引、唯一二级索引、普通二级索引)

  2. 匹配类型(精确匹配、唯一匹配、范围匹配)

  3. 事务隔离级别

  4. 是否开启 Innodb_locks_unsafe_for_binlog 系统变量

  5. 记录是否被标记删除

  6. 具体的执行语句类型(SELECT、INSERT、DELETE、UPDATE)

为了让文章相对易懂一些,我准备重点测试索引类型与匹配类型两个影响因素。对于其他的影响因素,我将不做改动。例如:事务隔离级别固定为「可重复读」,Innodb_locks_unsafe_for_binlog 固定为 false。而第 5、6 点相对来说简单一些,则我们会简单带过。

针对上面几个影响因素,我们指定了几个测试实验,分别是:

  1. 聚簇索引 + 精确匹配

  2. 聚簇索引 + 范围匹配

  3. 唯一二级索引 + 精确匹配

  4. 唯一二级索引 + 范围匹配

  5. 普通二级索引 + 精确匹配

  6. 普通二级索引 + 范围匹配

// 表结构
CREATE TABLE `test`.`price_test` (`id` BIGINT(64) NOT NULL AUTO_INCREMENT,`price` INT(4) NULL,PRIMARY KEY (`id`));
// 表中数据
1, apple, 10
2, orange, 30
50, perl, 60

聚簇索引 + 精确匹配

为了测试「聚簇索引 + 精确匹配」下加锁的类型,我们采用如下的测试方法。

事务 A 执行下面命令:

begin;
select * from price_test where id = 2 for update;

执行 show engine innodb status\G; 查看锁信息如下图所示。

可以看到,其是对 id 为 2 的索引加了一个记录锁。

此时事务 B 执行下面命令:

beign;
update price_test set price = 25 where id = 2;

执行之后,我们会发现事务 B 阻塞住了。

那如果聚簇索引的值找不到对应的记录呢,将会是一个什么样的结果呢?

我们再来测试一下,开始之前记得将事务 A 和 B 回滚恢复。

事务 A 执行下面命令,其中 id 为 5 的记录是不存在的:

begin;
select * from price_test where id = 5 for update;

执行 show engine innodb status\G; 查看锁信息如下图所示。

可以看到,其加了一个间隙锁,该间隙锁应该是 (2, 50) 这个范围。

我们可以通过在事务 B 执行如下命令来测试下间隙锁的范围。

beign;
// 执行下面任何一个命令,可以通过
update price_test set price = 25 where id = 2;
update price_test set price = 25 where id = 50;
// 执行下面任何一个命令,都将阻塞
insert into price_test(id,name,price) values(3,"test",25);
insert into price_test(id,name,price) values(5,"test",25);
insert into price_test(id,name,price) values(49,"test",25);

由此我们可以得出结论:「聚簇索引 + 精确匹配」,如果能够定位到唯一一条存在的记录,那么其会使用记录锁。如果该记录不存在,那么则会使用间隙锁。

聚簇索引 + 范围匹配

事务 A 执行下面命令:

begin;
select * from price_test where id >= 2 for update;

执行 show engine innodb status\G; 查看锁信息如下图所示。

可以看到,事务 A 一共加了 3 个锁,其中 1 个记录锁,2 个 Next-Key 锁。其中 1 个记录锁是对 id 为 2 的索引加的锁,Next-Key 锁是对 (2, 50] 和 (50, 正无穷) 这两个区间加的锁。

在事务 B 执行下面命令可以验证间隙锁的加锁区间:

beign;
// 执行下面任意一条语句,都会阻塞
update price_test set price = 25 where id = 2;
update price_test set price = 25 where id = 50;
insert into price_test(id,name,price) values(5,"test",25);
insert into price_test(id,name,price) values(60,"test",25);

这里我们思考一下,如果范围匹配的值并不存在,那么会是什么情况呢?

即事务 A 执行如下语句,其中 id 为 5 的记录是不存在的。

begin;
select * from price_test where id >= 5 for update;

执行 show engine innodb status\G; 查看锁信息如下图所示。

可以看到,其实加了 2 个 Next-Key 锁,锁的范围应该是 (2, 50) 和 [50, + 无穷)。

此时事务 B 执行下面命令,应该都会阻塞。

beign;
// 执行下面任意一条语句,都会阻塞
update price_test set price = 25 where id = 50;
insert into price_test(id,name,price) values(5,"test",25);
insert into price_test(id,name,price) values(45,"test",25);
insert into price_test(id,name,price) values(60,"test",25);

由此我们可以得出结论:「聚簇索引 + 范围匹配」,会使用「记录锁 + 间隙锁 + Next-Key 锁」。

唯一二级索引 + 精确匹配

事务 A 执行下面命令:

begin;
select * from price_test where price = 10 for update;

执行 show engine innodb status\G; 查看锁信息如下图所示。

可以看到,其加的行级锁是 2 个记录锁,应该是 price = 10 这条索引记录的锁。

此时,如果在事务 B 执行下面命令:

beign;
// 执行下面任意一条语句,都会阻塞
update price_test set name = 'test-name' where price = 10;

执行之后,我们会发现事务 B 阻塞住了。

由此我们可以得出结论:唯一二级索引与聚簇索引非常类似,都只有一个唯一值,都是使用记录锁。

唯一二级索引 + 范围匹配

事务 A 执行下面命令:

begin;
select * from price_test where price >= 30 for update;

执行 show engine innodb status\G; 查看锁信息如下图所示。

可以看到,事务 A 一共有 5 个行锁,其中 3 个 Next-Key 锁, 2 个记录锁。大致可以猜测出两个记录锁分别是 price 为 30 和 60 的记录锁。3 个 Next-Key 锁则是 (10, 30)、(30,60)、(60, 正无穷)三个范围。

为了验证我们上面的结论,我们在事务 B 执行下面命令,每条 SQL 都会阻塞住:

beign;
// 执行下面任意一条语句,都会阻塞
update price_test set name = 'price30' where price = 30;
update price_test set name = 'price60' where price = 60;
insert into price_test(id,name,price) values(5,"test", 20);
insert into price_test(id,name,price) values(5,"test", 40);
insert into price_test(id,name,price) values(5,"test", 70);

执行之后,我们会发现事务 B 阻塞住了。

由此我们可以得出结论:「唯一二级索引 + 范围匹配」,会使用「记录锁 + 间隙锁 + Next-Key 锁」。

普通二级索引 + 精确匹配

事务 A 执行下面命令:

begin;
select * from price_test where name = 'apple' for update;

执行 show engine innodb status\G; 查看锁信息如下图所示。

可以看到,其不仅有一个记录锁,还有一个间隙锁。这里可以猜测记录锁是 apple 索引的记录锁,而间隙锁则是 (负无穷,orange) 的间隙锁。

我们可在事务 B 执行如下命令验证一下:

begin;
// 执行下面任意一条语句,都会阻塞
update price_test set name = 'apple-new' where name = 'apple';
insert into price_test(id,name,price) values(5,"aa", 20);
insert into price_test(id,name,price) values(5,"ha", 20);
// 执行下面的语句正常执行
update price_test set name = 'orange-new' where name = 'orange';
insert into price_test(id,name,price) values(5,"orb", 20);

之所以二级索引的精确匹配会有间隙锁,是因为二级索引可能匹配到多个。因此当匹配到一个的时候,会继续往后匹配,直到匹配到一个不符合的记录,随后就会以该不符合的记录(这里是 orange)作为值做一个间隙锁。

由此我们可以得出结论:「普通二级索引 + 精确匹配」,会使用「记录锁 + 间隙锁 + Next-Key 锁」。

普通二级索引 + 范围匹配

事务 A 执行下面命令:

begin;
select * from price_test where name >= 'orange' for update;

执行 show engine innodb status\G; 查看锁信息如下图所示。

从上图可以看到起一共有 2 个记录锁,3 个 Next-Key 锁。其中 2 个记录锁应该是 orange 和 perl 两个记录,3 个 Next-Key 锁,应该是 (apple, orange]、[orange, perl)、[perl, 正无穷)。

我们可在事务 B 执行如下命令验证一下:

begin;
// 执行下面任意一条语句,都会阻塞
// 验证记录锁
update price_test set price = 1 where name = 'orange';
update price_test set price = 1 where name = 'perl';
// 验证间隙锁
insert into price_test(id,name,price) values(5,"ba", 20);
insert into price_test(id,name,price) values(5,"orb", 20);
insert into price_test(id,name,price) values(5,"pes", 20);
// 执行下面的语句正常执行
update price_test set price = 1 where name = 'apple';
insert into price_test(id,name,price) values(5,"aa", 20);

可以看到「普通二级索引 + 范围匹配」与「普通二级索引 + 精确匹配」结果是类似的。

我们可以得出结论:「普通二级索引 + 范围匹配」,会使用「记录锁 + 间隙锁 + Next-Key 锁」。

总结

我们做了这么多个测试,虽然有 3 种索引类型(聚簇索引、唯一二级索引、普通二级索引)和 2 种匹配类型(精确匹配、范围匹配),它们两两组合可以得出 6 种情况,再加上查询的值是否存在,可能有更多的可能性。但是我们发现它们的结构都非常类似,基本上都跟查找的记录是否存在,以及查找的记录是否是唯一的相关。

由此,我们大致可以得出结论:

  1. 如果查找的记录是唯一且存在的,那么只会使用记录锁,而不会使用间隙锁或 Next-Key 锁。

  2. 如果查找的记录不唯一或者不存在,那么就会使用 Next-Key 锁和间隙锁。


http://chatgpt.dhexx.cn/article/N9YLgs1y.shtml

相关文章

MySQL的间隙锁

一、、为什么要引入间隙锁 MySQL引入间隙锁(Gap Lock),是为了在可重复读事务隔离级别中,解决幻读问题锁引入的锁机制。 二、幻读 2.1、什么是幻读 幻读是指,当一个事务先后两次查询同一个范围的时候,查…

MySQL间隙锁(幻读解决原理)

文章目录 一、间隙锁概念二、测试间隙锁范围加锁场景1:用不可重复的主键id测试间隙锁场景2:用可重复的age(有索引)测试间隙锁场景3:实际情况需要具体分析用的到底是行锁还是表锁 三、测试等值间隙锁1. 测试不能重复的主…

mysql隔离级别RR下的行锁、临键锁、间隙锁详解及运用

一:mysql 锁的基本概念 锁:悲观锁、乐观锁 悲观锁:写锁 for update、读锁for share 写锁:只允许当前事务读写,其它事务全部等待,包括读取数据,锁的数据范围需要具体分析 读锁:允…

MySQL行锁、表锁、间隙锁详解

前言 我们前几篇讲了索引是什么,如何使用explain分析索引使用情况,如何去优化索引,以及show profiles分析SQL语句执行资源消耗的学习。今天我们来讲讲MySQL的各种锁,这里存储引擎我们使用InnoDB; 准备工作 创建表 t…

Mysql_行锁、临键锁、间隙锁的理解

目录 行锁间隙锁临键锁总结 行锁 行锁,也称为记录锁。 当我们针对主键或者唯一索引加锁的时候,Mysql默认会对查询的这一行数据加行锁,避免其他事务对这一行数据进行修改。 间隙锁 间隙锁,顾名思义,就是锁定一个索引…

【MySQL】MySQL的锁机制 - 记录锁、间隙锁、临键锁

目录 一、参考链接二、总结1、间隙锁的目的2、什么时候产生间隙锁?3、间隙锁锁定的范围4、需要注意的点(重要) 三、案例建表案例1:查询已存在的单条记录案例2:查询不存在的记录案例3:查询多条记录(范围查询) 四、其余验…

MySQL的锁机制 - 记录锁、间隙锁、临键锁

记录锁(Record Locks) 记录锁是 封锁记录,记录锁也叫行锁,例如: SELECT * FROM test WHERE id1 FOR UPDATE; 它会在 id1 的记录上加上记录锁,以阻止其他事务插入,更新,删除 id1 这一行。 记录锁、间隙锁…

行锁、间隙锁、next-key锁

参考博客:cmysql锁(九)innodb下的记录锁,间隙锁,next-key锁 - 简书 Mysql 行锁、间隙锁和next-key锁详解_程序员掉头发的博客-CSDN博客_行锁 间隙锁 行锁(Record Lock): 对索引记录加锁。间隙锁(Gap Lock…

mysql记录锁、间隙锁、临键锁

名词解释 记录锁:record lock,即锁住一条记录 间隙锁:gap lock,即锁定一个区间,左开又开 临键锁:记录锁间隙锁锁定的区间,左开右闭 mysql如何加锁 假设有如下数据 其中,id 是主键索引(唯一索…

【MySQL】记录锁?间隙锁?临键锁?到底锁了些什么?这一篇帮你捋清楚( ̄∇ ̄)/

特别强调~ 本测试使用的是MySQL 8.0.27~ 8.0.27~ 8.0.27(因为不同版本命令可能会有差异哈) 打开两个终端,分别连接上MySQL,使用select global.transaction_isolation;查看隔离级别&#xff08…

MySQL的锁机制——记录锁、间隙锁、临键锁

记录锁(Record Locks) 记录锁锁住的是索引记录,记录锁也叫行锁。如果使用索引作为条件命中了记录,那么就是记录锁,被锁住的记录不能被别的事务插入相同的索引键值,修改和删除。 例如: select * from test_table whe…

mysql 间隙锁

一、什么是间隙锁? 间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。 当我们用范围条件而不是相等条件索引数据,并请求…

什么是间隙锁

中心思想 间隙锁锁的是索引叶子节点的next指针。 意义 解决了mysql RR级别下是幻读的问题。 快照读 在RR隔离级别下:快照读有可能读到数据的历史版本,也有可能读到数据的当前版本。所以快照读无需用锁也不会发生幻读的情况。 当前读 当前读&#…

MySql进阶-间隙锁(gap-key)

文章目录 Innodb锁算法关闭Gap LockGap-key 解决的问题间隙锁影响MVCC 核心原理ReadView 可参考 快照读,当前读可参考 参考《InnoDB存储引擎》 注意:gap-key是innodb存储引擎来解决当前读的幻读问题的。对于隔离级别下的可重复读只能解决快照读的幻读问题…

dubbo接口调试工具

最近的项目使用来dubbo进行开发,虽然可以使用telne客户端t进行dubbo的接口的调试,但总感觉调试起来不太方便,并且限制太多,于是抽了点时间出来编写了一个dubbo的客户端可视化调试工具,功能虽简单但可以快速的调试dubbo…

Android Studio调试工具总结

前言:写代码不可避免有Bug,通常情况下除了日志最直接的调试手段就是debug;当我们的程序出现bug时,调试可以快速的找到bug。进入调试状态,我们可以清楚的了解程序的整个执行过程,可以对内存的数据进行监视。…

串口调试、udp 调试、tcp 调试,websocket 调试,通讯调试工具

简介 一个通讯测试工具,QSAK(Qt Swiss Army Knife)是一款基于Qt开源框架打造的多功能、跨平台调试工具。目前支持串口调试、udp 调试、tcp 调试及 websocket 调试等。支持 Windows、Linux、raspberry pi 等平台。 免费、开源、绿色、免安装…

chrome--浏览器调试工具详解

chrome浏览器开发调试工具打开方式:F12键 一,常用面板介绍 1.定位小箭头按钮(左边第一个): 选中Elements面板,并启动该按钮,可以在页面中定位相应元素的源代码位置,或者选择源代码位置可定位到页面相应的元…

通讯调试工具推荐

CommBox通讯调试工具-简单说明 工具支持:串口通讯、串口代理、TCP、UDP、Telnet、Ping、TFtp、串口监视、以太网嗅探等通讯测试 1、本工具支持预定义命令,命令可以进行分组,由树形控件管理。点击“命令编辑”即可编辑预…

modbus调试工具的使用

modbus调试工具的使用分为2部分,2者互相不影响,可以单独使用。 一、根据IP进行模拟通信 二、根据COM进行模拟通信 一、根据IP进行模拟通信 1.安装完成ModbusPollSetup64Bit后,输入注册码进行注册;安装完成ModbusSlaveSetup64Bi…