我做了一天的实验!
时间:2021-09-03 10:13:02
手机看文章
扫描二维码
随时随地手机看文章
[导读]大家好,我是小林。在前一篇文章我讲了下MySQL的全局锁、表记锁和行级别锁,其中行级锁只提了概念,并没有具体说。因为行级锁加锁规则比较复杂,不同的场景,加锁的形式还不同,所以这次就来好好介绍下行级锁。对记录加锁时,加锁的基本单位是next-keylock,它是由记录锁和间隙锁组合...
大家好,我是小林。在前一篇文章我讲了下 MySQL 的全局锁、表记锁和行级别锁,其中行级锁只提了概念,并没有具体说。因为行级锁加锁规则比较复杂,不同的场景,加锁的形式还不同,所以这次就来好好介绍下行级锁。对记录加锁时,加锁的基本单位是 next-key lock,它是由记录锁和间隙锁组合而成的,next-key lock 是前开后闭区间,而间隙锁是前开后开区间。但是,next-key lock 在一些场景下会退化成记录锁或间隙锁。那到底是什么场景呢?今天,我们就以下面这个表来进行实验说明。其中,id 是主键索引(唯一索引),b 是普通索引(非唯一索引),a 是普通的列。注意,我的 MySQL 的版本是 8.0.26,不同版本的加锁规则可能是不同的。
select * from t_test where id>=8 and id<9 for update;
做个实验就知道了。会话 1 加锁变化过程如下:
唯一索引等值查询
当我们用唯一索引进行等值查询的时候,查询的记录存不存在,加锁的规则也会不同:- 当查询的记录是存在的,在用「唯一索引进行等值查询」时,next-key lock 会退化成「记录锁」。
- 当查询的记录是不存在的,在用「唯一索引进行等值查询」时,next-key lock 会退化成「间隙锁」。
先看看记录是存在的。来看下面这个例子:会话1加锁变化过程如下:
- 加锁的基本单位是 next-key lock,因此会话1的加锁范围是(8, 16];
- 但是由于是用唯一索引进行等值查询,且查询的记录存在,所以 next-key lock 退化成记录锁,因此最终加锁的范围是 id = 16 这一行。
接下来,看看记录不存在的情况来看看,下面这个例子:会话1加锁变化过程如下:
- 加锁的基本单位是 next-key lock,因此主键索引 id 的加锁范围是(8, 16];
- 但是由于查询记录不存在,next-key lock 退化成间隙锁,因此最终加锁的范围是 (8,16)。
唯一索引范围查询
范围查询和等值查询的加锁规则是不同的。举个例子,下面这两条查询语句,查询的结果虽然是一样的,但是加锁的范围是不一样的。select * from t_test where id=8 for update;select * from t_test where id>=8 and id<9 for update;
做个实验就知道了。会话 1 加锁变化过程如下:
- 最开始要找的第一行是 id = 8,因此 next-key lock(4,8],但是由于 id 是唯一索引,且该记录是存在的,因此会退化成记录锁,也就是只会对 id = 8 这一行加锁;
- 由于是范围查找,就会继续往后找存在的记录,也就是会找到 id = 16 这一行停下来,然后加 next-key lock (8, 16],但由于 id = 16 不满足 id < 9,所以会退化成间隙锁,加锁范围变为 (8, 16)。
非唯一索引等值查询
当我们用非唯一索引进行等值查询的时候,查询的记录存不存在,加锁的规则也会不同:- 当查询的记录存在时,除了会加 next-key lock 外,还额外加间隙锁,也就是会加两把锁。
- 当查询的记录不存在时,只会加 next-key lock,然后会退化为间隙锁,也就是只会加一把锁。
我们先来看看查询的值存在的情况。比如下面这个例子:会话 1 加锁变化过程如下:
- 先会对普通索引 b 加上 next-key lock,范围是(4,8];
- 然后因为是非唯一索引,且查询的记录是存在的,所以还会加上间隙锁,规则是向下遍历到第一个不符合条件的值才能停止,因此间隙锁的范围是(8,16)。
接下来,我们看看查询的值不存在的情况直接看案例:会话 1 加锁变化过程如下:
- 先会对普通索引 b 加上 next-key lock,范围是(8,16];
- 但是由于查询的记录是不存在的,所以不会再额外加个间隙锁,但是 next-key lock 会退化为间隙锁,最终加锁范围是 (8,16)。
非唯一索引范围查询
非唯一索引和主键索引的范围查询的加锁也有所不同,不同之处在于普通索引范围查询,next-key lock 不会退化为间隙锁和记录锁。来看下面这个案例:会话 1 加锁变化过程如下:- 最开始要找的第一行是 b = 8,因此 next-key lock(4,8],但是由于 b 不是唯一索引,并不会退化成记录锁。
- 但是由于是范围查找,就会继续往后找存在的记录,也就是会找到 b = 16 这一行停下来,然后加 next-key lock (8, 16],因为是普通索引查询,所以并不会退化成间隙锁。
总结
这次我以 MySQL 8.0.26 版本做了几个实验,让大家了解了唯一索引和非唯一索引的行级锁的加锁规则。这里需要注意的是,不同的版本加锁规则可能会有所不同。我这里总结下, 我这个 MySQL 版本的行级锁的加锁规则。唯一索引等值查询:- 当查询的记录是存在的,next-key lock 会退化成「记录锁」。
- 当查询的记录是不存在的,next-key lock 会退化成「间隙锁」。
- 当查询的记录存在时,除了会加 next-key lock 外,还额外加间隙锁,也就是会加两把锁。
- 当查询的记录不存在时,只会加 next-key lock,然后会退化为间隙锁,也就是只会加一把锁。
- 唯一索引在满足一些条件的时候,next-key lock 退化为间隙锁和记录锁。
- 非唯一索引范围查询,next-key lock 不会退化为间隙锁和记录锁。