mysql 间隙锁


在学习mysql锁过程中有些想法记录与分享

环境:mysql5.6  innodb存储引擎,默认隔离级别repeatable-read,可重复读。innodb_locks_unsafe_for_binlog, 参数默认值是OFF,也就是启用间隙锁, 他是一个bool值, 当值为true时表示disable间隙锁

知识:与oracle不同的是,mysql加锁是对索引加锁

在进行删除或者修改操作时,如果过滤条件列是非唯一索引,为了保证当前读的数据一致性,mysql通过间隙锁对数据之间区域进行锁定。(实际上是通过锁定索引达到效果)

这种锁叫间隙锁,这种锁定会造成许多误杀,很多并不冲突的数据会因为间隙锁而无法插入。

 

举例:

准备测试数据

create table test (id int,name varchar(10));
alter table test add primary key PK_ID (id);
alter table test add index INX_Name (name);
insert into test values (1,'a1');
insert into test values (2,'a1');
insert into test values (3,'a1');
insert into test values (4,'a3');
insert into test values (5,'a4');
insert into test values (11,'a6');
insert into test values (21,'a10');
insert into test values (22,'a13');
insert into test values (23,'a14');
insert into test values (24,'a15');

session one;

 

 

删除name='a3',因为间隙锁的缘故,会对 (24,a15)和(4,a3)之间加锁,(4,a3)和(5,a4)之间加锁。

很明显,如果我们想插入a2到name字段中,必然会因为间隙锁,而处于等待状态。

 

测试:session two

 

insert into test(id,name) values (30,'a2'); 插入等待,并且锁等待超时。

 

同样a3,a4之间插入数据也会处于等待超时。

 

这时很容易想到一个问题,如果间隙锁是在两组数据中间加锁,那么如果我插入a3两边的数据,a15或者a4,是否会出现锁等待呢。

继续做测试:

继续插入:insert into test(id,name) values(20,'a15');

                 insert into test(id,name) values(26,'a15');

可以看到插入ID 20很快成功,插入ID 26 出现锁等待。

同样是a15,因为ID不同,结果不一样,是什么原因呢。

我们来看插入后的一个查询,很容易就理解了:

这个查询是按照name列的索引做的顺序排序,我们可以看到 (20,a15)已经插入成功。

我们已经知道在(24,a15)和(4,a3)之间存在间隙锁,如果数据(26,a15)需要插入进去,那么必然会排在24 a15之后

形成:

20   a15

24   a15

26   a15

4     a3

而这样的数据必然会被间隙锁阻塞,因此导致锁等待。

所以我们在上面明确间隙锁的时候,需要书写一组数据(主键列,辅助索引列),而不是单单只写一列。

 

最后得出结论:间隙锁锁定了辅助索引两个叶子节点之间的内容,会造成锁定多余区域的数据。

先delete再insert的操作时常会导致死锁情况的出现。

应用程序sql中,在做删除或者更新操作中,对于过滤条件尽量选择主键列或者唯一索引列

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM