【MySQL】间隙锁实战

214 阅读2分钟

「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战」。

当使用范围条件而不是相等条件检索数据,并请求共享或排它锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,称为“间隙(gap)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙(Next-Key)锁。

准备表格

我们在 SQL 操作中演示一下 MySQL 的间隙锁。

打开两个终端,终端 1 将执行删除操作,终端 2 将执行插入操作。

首先创建一张表:

create teble gap(id tinyint primary key, val int);

然后插入一些数据:

insert into gap values(2,2), (3,3), (17,17);

区间导致的间隙锁

接下来开始演示间隙锁,使用终端 1 开启一个事务,执行删除 id 大于 2 小于 7 的记录的操作,不要提交,代码为:

begin;
delete from gap where id > 2 and id < 7;

使用终端 2 插入一条 id 为 5 的数据,代码为:

insert into gap values(5, 5);

此时,终端 1 的事务会得到一个锁定了范围 (2,17)(2, 17) 的间隙锁,当我们对 id 落在这一范围的记录进行插入和更新操作时,这一操作就会被阻塞。如下图所示,上方终端为终端 2 ,下方终端为终端 1。

屏幕截图 2022-02-04 155522.jpg

只有当终端 1 的事务提交或回滚之后,终端 2 的操作才能执行。如下图所示,终端 1 事务回滚后,终端 2 操作执行。由于之前终端 2 的操作被阻塞,因此执行耗时很长(40.63秒)。

屏幕截图 2022-02-04 155613.jpg

不存在的键导致的间隙锁

如果在执行更新和删除操作时,对不存在的键进行操作,此时也会导致间隙锁的出现。

我们在执行了之前的操作后,表 gap 的数据如下:

Inked屏幕截图 2022-02-04 162331_LI.jpg

使用终端 1 开启一个事务,执行删除 id 为 10 的记录的操作,不要提交,代码为:

begin;
delete from gap where id = 10;

此时,表中并没有 id 为 10 的记录。于是,终端 1 的事务会得到一个锁定了范围 (5,17)(5, 17) 的间隙锁。当终端 2 试图插入 id 落在这一区间的记录时,操作就会被阻塞,如上图所示。

如果终端 2 试图插入 id 为 5 或 17 的记录,则不受锁的影响,如下图所示:

屏幕截图 2022-02-04 162604.jpg