在学习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中,在做删除或者更新操作中,对于过滤条件尽量选择主键列或者唯一索引列