谈谈InnoDB下的记录锁,间隙锁,next-key锁

innodb下的记录锁(也叫行锁),间隙锁,next-key锁统统属于排他锁。行锁,即记录锁,其实很好理解,对表中的记录加锁,叫做记录锁,简称行锁。就不多做介绍,主要介绍间隙锁,next-key锁。

间隙锁

间隙锁,顾名思义就是存在间隙,在间隙之间上锁保证在间隙间不可以进行操作。举一个生活中比较好理解得例子,A,B,C依次排成一排,为了让新来的D不能插在B的旁边,只要将B和A之间的空隙封锁,将B和C之间的空隙封锁,那么D就不能插在B的旁边了。他们之间的空隙也就是间隙,而封锁他们之间距离的锁,叫做间隙锁。这里映射到数据库层面,A,B,C,D就是数据库的一条条记录。

Mysql中的间隙锁

假设数据库中某个表存在以下数据,其中id为主键,number字段上有非唯一索引的二级索引,有什么方式可以让该表不能再插入number=5的记录呢?

id(主键)number(二级索引)
12
34
65
85
105
1311

由之前的概念可以想到,只要控制number=5之前不能插入记录,number=5现有的记录之间不能再插入新的记录,number=5之后不能插入新的记录,那么新的number=5的记录将不能被插入进来。那么,mysql是如何控制number=5之前,之中,之后不能有新的记录插入呢(防止幻读)?答案是用间隙锁。
在RR级别下,mysql通过间隙锁可以实现锁定number=5之前的间隙,number=5记录之间的间隙,number=5之后的间隙,从而使的新的记录无法被插入进来。间隙锁要通过两个方面实现防止幻读:

  • 防止间隙内有新数据被插入
  • 防止已存在的数据,更新成间隙内的数据
    接下来带大家来理解一下具体的含义。

间隙锁的间隙划分

为了方便理解,我们规定(id=A,number=B)代表一条字段id=A,字段number=B的记录,(C,D)代表一个区间,代表C-D这个区间范围。所以在上图中,根据number列,可以分为几个区间:(无穷小,2),(2,4),(4,5),(5,5),(5,11),(11,无穷大)。只要这些区间对应的两个临界记录中间可以插入记录,就认为区间对应的记录之间有间隙。例如区间(2,4)分别对应的临界记录是(id=1,number=2),(id=3,number=4),这两条记录中间可以插入(id=2,number=3)等记录,那么就认为(id=1,number=2)与(id=3,number=4)之间存在间隙。同理(id=6,number=5)与(id=8,number=5)之间可以插入记录(id=7,number=5),因此(id=6,number=5)与(id=8,number=5)之间有间隙的。

间隙锁的锁定区域

根据检索条件向左寻找最靠近检索条件的记录值A,作为左区间,向右寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。如上图中,where number=5的话,那么间隙锁的区间范围为(4,11)。

间隙锁的实际操作

接下来我们通过几个具体的例子来观察下间隙锁的作用范围。

实际操作一

有如下两个事务操作,我们分析一下当session 1执行的时候,session 2中的insert操作是否可以执行成功。

session 1:
start  transaction ;
select  * from news where number=4 for update ;

session 2:
start  transaction ;
insert into news value(2,4);
insert into news value(2,2);
insert into news value(4,4);
insert into news value(4,5);
insert into news value(7,5);
insert into news value(9,5);
insert into news value(11,5);

检索条件number=4,向左取得最靠近的值2作为左区间,向右取得最靠近的5作为右区间,因此,session 1的间隙锁的范围(2,4),(4,5),如下图所示:
在这里插入图片描述
间隙锁锁定的区间为(2,4),(4,5),即记录(id=1,number=2)和记录(id=3,number=4)之间间隙会被锁定,记录(id=3,number=4)和记录(id=6,number=5)之间间隙被锁定。因此记录(id=2,number=4),(id=2,number=2),(id=4,number=4),(id=4,number=5)正好处在(id=3,number=4)和(id=6,number=5)之间,所以插入不了,需要等待锁的释放,而记录(id=7,number=5),(id=9,number=5),(id=11,number=5)不在上述锁定的范围内,因此都会插入成功。

session 2:
start  transaction ;
insert into news value(2,4);#(阻塞)
insert into news value(2,2);#(阻塞)
insert into news value(4,4);#(阻塞)
insert into news value(4,5);#(阻塞)
insert into news value(7,5);#(执行成功)
insert into news value(9,5);#(执行成功)
insert into news value(11,5);#(执行成功)
实际操作二

有如下两个事务操作,我们同样来分析一下当session 1执行的时候,session 2中的insert操作是否可以执行成功。

session 1:
start  transaction ;
select  * from news where number=13 for update ;

session 2:
start  transaction ;
insert into news value(11,5);
insert into news value(12,11);
insert into news value(14,11);
insert into news value(15,12);
update news set id=14 where number=11;
update news set id=11 where number=11;

检索条件number=13,向左取得最靠近的值11作为左区间,向右由于没有记录因此取得无穷大作为右区间,因此,session 1的间隙锁的范围(11,无穷大),如下图所示:
在这里插入图片描述
此表中没有number=13的记录的,innodb依然会为该记录左右两侧加间隙锁,间隙锁的范围(11,无穷大)。因此记录(id=11,number=5),(id=12,number=11),(id=11,number=11),处在(id=13,number=11)之前,所以会插入成功,而记录(id=14,number=11),(id=15,number=12),(id=14,number=11)在上述锁定的范围内,因此操作阻塞,需要等待锁的释放。

session 2:
start  transaction ;
insert into news value(11,5);#(执行成功)
insert into news value(12,11);#(执行成功)
insert into news value(14,11);#(阻塞)
insert into news value(15,12);#(阻塞)
update news set id=14 where number=11;#(阻塞)
update news set id=11 where number=11;#(执行成功)

可能有人还是不太明白,为啥update news set id=14 where number=11会阻塞,但是update news set id=11 where number=11却执行成功呢?间隙锁采用在指定记录的前面和后面以及中间的间隙上加间隙锁的方式避免数据被插入,此图间隙锁锁定的区域是(11,无穷大),也就是记录(id=13,number=11)之后不能再插入记录,update news set id=14 where number=11这条语句如果执行的话,将会被插入到(id=13,number=11)的后面,也就是在区间(11,无穷大)之间,由于该区间被间隙锁锁定,所以只能阻塞等待,而update news set id=11 where number=11执行后是会被插入到(id=13,number=11)的记录前面,也就不在(11,无穷大)的范围内,所以无需等待,执行成功。

实际操作三

有如下两个事务操作,我们同样来分析一下当session 1执行的时候,session 2中的insert操作是否可以执行成功。

session 1:
start  transaction ;
select  * from news where number=5 for update;

session 2:
start  transaction ;
insert into news value(4,4);
insert into news value(4,5);
insert into news value(5,5);
insert into news value(7,11);
insert into news value(9,12);
insert into news value(12,11);
update news set number=5 where id=1;
update news set id=11 where number=11;
update news set id=2 where number=4 ;
update news set id=4 where number=4 ;

检索条件number=5,向左取得最靠近的值4作为左区间,向右取得11为右区间,因此,session 1的间隙锁的范围(4,5),(5,11),如下图所示:
在这里插入图片描述
依据之前的分析方法,我想大家很快就可以得到一下的结论:

session 2:
start  transaction ;
insert into news value(4,4);#(阻塞)
insert into news value(4,5);#(阻塞)
insert into news value(5,5);#(阻塞)
insert into news value(7,11);#(阻塞)
insert into news value(9,12);#(执行成功)
insert into news value(12,11);#(阻塞)
update news set number=5 where id=1;#(阻塞)
update news set id=11 where number=11;#(阻塞)
update news set id=2 where number=4 ;#(执行成功)
update news set id=4 where number=4 ;#(阻塞)

有人会问,为啥insert into news value(9,12)会执行成功?间隙锁采用在指定记录的前面和后面以及中间的间隙上加间隙锁的方式避免数据被插入,(id=9,number=12)很明显在记录(13,11)的后面,因此不再锁定的间隙范围内。而为啥update news set number=5 where id=1会阻塞?number=5的记录的前面,后面包括中间都被封锁了,你这个update news set number=5 where id=1根本没法执行,因为innodb已经把你可以存放的位置都锁定了,因为只能等待。同理,update news set id=11 where number=11由于记录(id=10,number=5)与记录(id=13,number=11)中间的间隙被封锁了,你这句sql也没法执行,必须等待,因为存放的位置被封锁了。

实际操作三

有如下两个事务操作,我们同样来分析一下当session 1执行的时候,session 2中的insert操作是否可以执行成功。

session 1:
start  transaction;
select * from news where number>4 for update;

session 2:
start  transaction;
update news set id=2 where number=4 ;
update news set id=4 where number=4 ;
update news set id=5 where number=5 ;
insert into news value(2,3);
insert into news value(null,13);

检索条件number>4,向左取得最靠近的值4作为左区间,向右取无穷大,因此,session 1的间隙锁的范围(4,无穷大),如下图所示:
在这里插入图片描述
同样依据之前的分析方式,也很容易得出以下的结论:

session 2:
start  transaction;
update news set id=2 where number=4 ;#(执行成功)
update news set id=4 where number=4 ;#(阻塞)
update news set id=5 where number=5 ;#(阻塞)
insert into news value(2,3);#(执行成功)
insert into news value(null,13);#(阻塞)

next-key锁

next-key锁其实包含了记录锁和间隙锁,即锁定一个范围,并且锁定记录本身,InnoDB默认加锁方式是next-key 锁。例如上面的实际操作一中的session 1的sql:

select * from news where number=4 for update ;

next-key锁锁定的范围为间隙锁+记录锁,即在区间(2,4),(4,5)加间隙锁,同时number=4的记录加记录锁。比较简单,就不做过多叙述。

猜你感兴趣
MYSQL专题-绝对实用的MYSQL优化总结
MYSQL专题-MySQL事务实现原理
MYSQL专题-MVCC多版本并发控制
MYSQL专题-MySQL三大日志binlog、redo log和undo log

更多文章请点击:更多…

参考文章:
https://www.jianshu.com/p/bf862c37c4c9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值