一文详解 MySQL 中的间隙锁
博客:https://www.emanjusaka.com
博客园:https://www.cnblogs.com/emanjusaka
公众号:emanjusaka的编程栈
by emanjusaka from https://www.emanjusaka.com/archives/mysql-gap-lock 彼岸花开可奈何
本文为原创文章,可能会更新知识点以及修正文中的一些错误,全文转载请保留原文地址,避免产生因未即时修正导致的误导。
间隙锁(Gap Lock)是(RR 级别下)一个在索引记录之间的间隙上的锁,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。
间隙锁是 MySQL 行锁的一种,与行锁不同的是间隙锁可能锁定的是一行数据,也可能锁住一个间隙。
触发条件
事务隔离级别为 REPEATABLE-READ(可重复读),间隙锁是为了防止幻读。
使用范围查询条件
使用等值查询但未命中记录时,如果查询的条件列上有索引
索引不唯一的情况
锁定区间
间隙锁会向左找第一个比当前索引值小的值,向右找第一个比当前索引值大的值(没有则为正无穷),将此区间锁住,从而阻止其他事务在此区间插入数据
间隙锁(Gap Lock),左右都是开区间,间隙锁 + 行锁组合成 Next-key lock,左开右闭区间。
加锁规则
加锁的基本单位是 next-key lock,它是左开右闭区间
查找过程中访问到的对象才会加锁
唯一索引上的等值查询,并且记录存在,next-key lock 退化为行锁
唯一索引上的范围查询会访问到不满足条件的第一个值为止
索引上的等值查询,会将距离最近的左边界和右边界作为锁定范围,如果索引不是唯一索引还会继续向右匹配,直到遇见第一个不满足条件的值,如果最后一个值不等于查询条件,Next-Key Lock 退化为间隙锁。
注意,delete 语句加锁的逻辑,其实跟 select ... for update 是类似的。
这些加锁规则并不全部适应 MySQL 的全部版本,在新版本的 MySQL 中可能会有一些变动,大体上是符合这些规则的。
我们也可以执行select * FROM performance_schema.data_locks;来查看具体加了什么锁。
LOCK_TYPE
TABLE:表锁
RECORD:行级锁
LOCK_MODE
X:next-key 锁
X,REC_NOT_GAP:记录锁
X,GAP:间隙锁
间隙锁的作用
与行锁(例如乐观锁高级实现,MVCC)组合成 Next-key lock,在可重复读这种隔离级别下一起工作避免幻读。
案例演示
环境:MySQL8.3.0,InnoDB引擎, RR 隔离级别
表结构:
CREATE TABLE t (
id int(11) NOT NULL,
c int(11) DEFAULT NULL,
d int(11) DEFAULT NULL,
PRIMARY KEY (id),
KEY c (c)
) ENGINE=InnoDB;
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20);
| id | c | d |
|---|---|---|
| 0 | 0 | 0 |
| 5 | 5 | 5 |
| 10 | 10 | 10 |
| 15 | 15 | 15 |
| 20 | 20 | 20 |
存在的间隙:
(-∞,0]
(0,5]
(5,10]
(10,15]
(15,20]
(20,+supremum]
InnoDB 给每个索引加了一个不存在的最大值 supremum,这样才符合“左开右闭区间”。
唯一索引等值锁定存在的数据
| 时刻 | 事务 1 | 事务 2 |
|---|---|---|
| t1 | begin; select * from t where id = 5 for update; | |
| t2 | begin; insert into t value(4,4,4); --- success | |
| insert into t value(6,6,6); --- success |
根据规则 3,事务 1 中执行的 select 语句,会退化成行锁,只会锁定 5 这一行记录

索引等值锁定
| 时刻 | 事务 1 | 事务 2 |
|---|---|---|
| t1 | begin; select * from t where id = 3 for update; --- 不存在的数据 | |
| t2 | begin; insert into t value(6,6,6); --- success | |
| t3 | insert into t value(4,4,4); --- block |
根据规则 1加锁范围是(0,5],再根据规则 5由于向右遍历最后一个值 5 不满足等值条件退化为间隙锁(0,5)
插入 数据6 不在间隙锁的范围内可以插入成功,而数据 4 是间隙锁锁定范围进入阻塞状态

唯一索引范围锁定
| 时刻 | 事务 1 | 事务 2 |
|---|---|---|
| t1 | begin; select * from t where id >= 5 and id < 6 for update; | |
| t2 | begin; insert into t value(7,7,7); --- block |
这个可以分为两部分看
首先看 id > = 5,锁定区间(0,5]根据规则 1 和规则 3 会退化成行锁
再看 id < 6 根据规则 4 锁定区间为(5,10]
综合两部分最终锁定区间[5,10]
注意:这个锁定区间在不同版本的 MySQL 中可能表现不同,在 8.3.0 版本中第二部分会退化成间隙锁,锁定区间变成了[5,10)

非唯一索引范围锁定
| 时刻 | 事务 1 | 事务 2 | 事务 3 |
|---|---|---|---|
| t1 | begin; select * from t where c >= 10 and c < 11 for update; | ||
| t2 | begin; insert into t values(7,7,7); --block | ||
| t3 | begin; update t set d = d + 1 where c = 15; --block |
用 c =10 定位记录,给索引 c 加上了 next-key lock (5,10],它不会退化成行锁,索引 c 不是唯一索引。因此最终索引 c 上加的(5,10] 和 (10,15]这两个 next-key lock

limit语句加锁
| 时刻 | 事务 1 | 事务 2 |
|---|---|---|
| t1 | begin; select * from t where c = 10 limit 1; | |
| begin; insert into t values(12,12,12); ---success |
如果没有 limit 语句的话在索引 c 上加锁范围是从(c=5,id=5)到 (c =15,id=15)。
但是因为加了 limit 1 的限制,因此在访问(c=10,id=10)这一行时,满足条件的语句已经有1 条了循环结束。
因此索引 c 上的加锁范围就变成了从(c=5,id=5)到(c=10,id=10)。


参考资料
谦学于心,谷纳万物,静思致远,共筑收获之旅!
原文地址: https://www.emanjusaka.com/archives/mysql-gap-lock
一文详解 MySQL 中的间隙锁的更多相关文章
- 详解MySQL中EXPLAIN解释命令
Explain 结果解读与实践 基于 MySQL 5.0.67 ,存储引擎 MyISAM . 注:单独一行的"%%"及"`"表示分隔内容,就象分开“第一 ...
- 一文详解MySQL的锁机制
一.表级锁.行级锁.页级锁 数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则. MySQL数据库由于其自身架构的特点,存在多种数据存储引擎, ...
- 详解MySQL中concat函数的用法(连接字符串)
MySQL中concat函数 使用方法: CONCAT(str1,str2,…) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. 注意: 如果所有参数均为非二进制 ...
- 详解 MySQL 中的 explain
来源:persister 链接:http://www.blogjava.net/persister/archive/2008/10/27/236813.html 在 explain的帮助下,您就知道什 ...
- 详解MySQL中EXPLAIN解释命令(转)
explain显示了mysql如何使用索引来处理select语句以及连接表.可以帮助选择更好的索引和写出更优化的查询语句. 使用方法,在select语句前加上explain就可以了: 如: expla ...
- 一文详解MySQL如何同时自增自减多个字段
本文将带大家聊一下如何同时自增自减多个字段 开始之前,先分享一套MySQL教程,小白入门或者学习巩固都可以看 MySQL基础入门-mysql教程-数据库实战(MySQL基础+MySQL高级+MySQL ...
- 高级程序员必知必会,一文详解MySQL主从同步原理,推荐收藏
1. MySQL主从同步实现方式 MySQL主从同步是基于Bin Log实现的,而Bin Log记录的是原始SQL语句. Bin Log共有三种日志格式,可以binlog_format配置参数指定. ...
- 详解mysql中的Using与On的用法
多用才可以体会各个关键字的用法啊... 原文来自[http://bbs.php100.com/read-htm-tid-148469.html] 在用Join进行多表联合查询时,我们通常使用On来建立 ...
- MySQL数据类型 int(M) 表示什么意思?详解mysql int类型的长度值问题
MySQL 数据类型中的 integer types 有点奇怪.你可能会见到诸如:int(3).int(4).int(8) 之类的 int 数据类型.刚接触 MySQL 的时候,我还以为 int(3) ...
- Mysql常用show命令,show variables like xxx 详解,mysql运行时参数
MySQL中有很多的基本命令,show命令也是其中之一,在很多使用者中对show命令的使用还容易产生混淆,本文汇集了show命令的众多用法. 详细: http://dev.mysql.com/doc/ ...
随机推荐
- Educational Codeforces Round 155 (Rated for Div
B. Chips on the Board 题解:贪心 显然我们可以把题意转化为:对于任意一个\((i,j)\),我们可以花费\(a_{i,j}\)的代价占据第\(i\)行和第\(j\)列,求占据所有 ...
- Three.js入门-常见几何体
这章节,我们将介绍 Three.js 中常见的几何体(Geometry),包括立方体.球体.圆柱体.平面.圆环.圆锥体等.几何体是构建 3D 模型的基础元素,通过不同的几何体可以创建出各种形状的物体. ...
- 打包jar 程序,同时将依赖报一起打包
1.概述 有些时候,我们打包一个java 程序,我需要将所有的依赖也一起打包,这样执行起来方便. 2.我们可以使用maven 插件实现 <build> <plugins> &l ...
- javascript的一些API接口的使用
1.blob http URL 在编辑器中,有的情况下插入图片,会讲图片转成 blob:http://localhost/*** 的这种形式.这种形式的URL实际数据是存放在浏览器的内存中. 这种情况 ...
- Numpy本征值求解
技术背景 Numpy是一个Python库中最经常被用于执行计算任务的一个包,得益于其相比默认列表的高性能表现,以及易用性和可靠性,深受广大Python开发者的喜爱.这里介绍的是使用Numpy计算矩阵本 ...
- 原创单总线传输协议b2s (附全部verilog源码)
一.b2s协议背景介绍 本单总线传输协议为精橙FPGA团队原创,含传送端(transmitter)和接收端(receiver)两部分,基于verilog语言,仅使用单个I/O口进行多位数据的传输,传输 ...
- Flutter自动生成安卓和IOS图标
flutter_launcher_icons 用这个库自动生成 首先添加依赖 dev_dependencies: flutter_launcher_icons: flutter_icons: andr ...
- 企架布道:中电金信应邀出席2023佛山敏捷之旅暨DevOps Meetup
近日,2023佛山敏捷之旅暨DevOps Meetup活动顺利举行,本次活动以助力大湾区金融和互联网企业敏捷DevOps实施和效能提升为主题,共设立 2个会场,16个话题分享,200余位金融.互联网企 ...
- Android信任证书,把用户级别放入系统级别
三.操作步骤 1.在Windows安装openssl,用来把证书转成 .pem 格式 1)下载和安装 下载其他人做的便捷版安装包:http://slproweb.com/products/Win32O ...
- Qt音视频开发30-qmedia内核qt4方案phonon播放(支持视频流)
一.前言 在Qt4中如果需要播放视频,一般用phonon多媒体框架,这应该就是Qt5/Qt6中多媒体框架的前身(查阅qmultimedia模块的相关代码可以发现架构几乎雷同,除了部分命名变了以外),p ...