MySQL的全局锁表锁和行锁

我国知名研究白癜风的专家 https://disease.39.net/bjzkbdfyy/170622/5481259.html
MySQL的全局锁、表锁和行锁在前一篇文章我讲了下MySQL的全局锁、表记锁和行级别锁,其中行级锁只提了概念,并没有具体说。因为行级锁加锁规则比较复杂,不同的场景,加锁的形式还不同,所以这次就来好好介绍下行级锁。对记录加锁时,加锁的基本单位是next-keylock,它是由记录锁和间隙锁组合而成的,next-keylock是前开后闭区间,而间隙锁是前开后开区间。但是,next-keylock在一些场景下会退化成记录锁或间隙锁。那到底是什么场景呢?今天,我们就以下面这个表来进行实验说明。其中,id是主键索引(唯一索引),b是普通索引(非唯一索引),a是普通的列。注意,我的MySQL的版本是8.0.26,不同版本的加锁规则可能是不同的。唯一索引等值查询当我们用唯一索引进行等值查询的时候,查询的记录存不存在,加锁的规则也会不同:

当查询的记录是存在的,在用「唯一索引进行等值查询」时,next-keylock会退化成「记录锁」。

当查询的记录是不存在的,在用「唯一索引进行等值查询」时,next-keylock会退化成「间隙锁」。

加锁的基本单位是next-keylock,因此会话1的加锁范围是(8,16];

但是由于是用唯一索引进行等值查询,且查询的记录存在,所以next-keylock退化成记录锁,因此最终加锁的范围是id=16这一行。

所以,会话2在修改id=16的记录时会被锁住,而会话插入id=9的记录可以被正常执行。

加锁的基本单位是next-keylock,因此主键索引id的加锁范围是(8,16];

但是由于查询记录不存在,next-keylock退化成间隙锁,因此最终加锁的范围是(8,16)。

所以,会话2要往这个间隙里面插入id=9的记录会被锁住,但是会话修改id=16是可以正常执行的,因为id=16这条记录并没有加锁。唯一索引范围查询举个例子,下面这两条查询语句,查询的结果虽然是一样的,但是加锁的范围是不一样的。

select*fromt_testwhereid=8forupdate;select*fromt_testwhereid=8andid9forupdate;

最开始要找的第一行是id=8,因此next-keylock(4,8],但是由于id是唯一索引,且该记录是存在的,因此会退化成记录锁,也就是只会对id=8这一行加锁;

由于是范围查找,就会继续往后找存在的记录,也就是会找到id=16这一行停下来,然后加next-keylock(8,16],但由于id=16不满足id9,所以会退化成间隙锁,加锁范围变为(8,16)。

所以,会话1这时候主键索引的锁是记录锁id=8和间隙锁(8,16)。会话2由于往间隙锁里插入了id=9的记录,所以会被锁住了,而id=8是被加锁的,因此会话的语句也会被阻塞。由于id=16并没有加锁,所以会话4是可以正常被执行。非唯一索引等值查询当我们用非唯一索引进行等值查询的时候,查询的记录存不存在,加锁的规则也会不同:

当查询的记录存在时,除了会加next-keylock外,还额外加间隙锁,也就是会加两把锁。

当查询的记录不存在时,只会加next-keylock,然后会退化为间隙锁,也就是只会加一把锁。

先会对普通索引b加上next-keylock,范围是(4,8];

然后因为是非唯一索引,且查询的记录是存在的,所以还会加上间隙锁,规则是向下遍历到第一个不符合条件的值才能停止,因此间隙锁的范围是(8,16)。

所以,会话1的普通索引b上共有两个锁,分别是next-keylock(4,8]和间隙锁(8,16)。那么,当会话2往间隙锁里插入id=9的记录就会被锁住,而会话和会话4是因为更改了next-keylock范围里的记录而被锁住的。然后因为b=16这条记录没有加锁,所以会话5是可以正常执行的。

先会对普通索引b加上next-keylock,范围是(8,16];

但是由于查询的记录是不存在的,所以不会再额外加个间隙锁,但是next-keylock会退化为间隙锁,最终加锁范围是(8,16)。

会话2因为往间隙锁里插入了b=9的记录,所以会被锁住,而b=16是没有被加锁的,因此会话的语句可以正常执行。非唯一索引范围查询非唯一索引和主键索引的范围查询的加锁也有所不同,不同之处在于普通索引范围查询,next-keylock不会退化为间隙锁和记录锁。

最开始要找的第一行是b=8,因此next-keylock(4,8],但是由于b不是唯一索引,并不会退化成记录锁。

但是由于是范围查找,就会继续往后找存在的记录,也就是会找到b=16这一行停下来,然后加next-keylock(8,16],因为是普通索引查询,所以并不会退化成间隙锁。

所以,会话1的普通索引b有两个next-keylock,分别是(4,8]和(8,16]。这样,你就明白为什么会话2、会话、会话4的语句都会被锁住了。总结这次我以MySQL8.0.26版本做了几个实验,让大家了解了唯一索引和非唯一索引的行级锁的加锁规则。这里需要注意的是,不同的版本加锁规则可能会有所不同。我这里总结下,我这个MySQL版本的行级锁的加锁规则。

当查询的记录是存在的,next-keylock会退化成「记录锁」。

当查询的记录是不存在的,next-keylock会退化成「间隙锁」。

当查询的记录存在时,除了会加next-keylock外,还额外加间隙锁,也就是会加两把锁。

当查询的记录不存在时,只会加next-keylock,然后会退化为间隙锁,也就是只会加一把锁。

非唯一索引和主键索引的范围查询的加锁规则不同之处在于:

唯一索引在满足一些条件的时候,next-keylock退化为间隙锁和记录锁。

非唯一索引范围查询,next-keylock不会退化为间隙锁和记录锁。

这些加锁规则其实很好总结的,大家自己可以用我文中的案例测试一遍,看一下你的MySQL版本和我的MySQL版本的加锁规则有什么不同。


转载请注明:http://www.aierlanlan.com/cyrz/8815.html