谈谈你对间隙锁的理解

间隙锁

简介

间隙锁是对数据库表一定范围上的加锁,它能和行锁组成next-key lock解决在可重复读事务隔离级别下产生的幻读问题。幻读就是一个事务在进行范围查询的时候,前后查询的结果不一样。

原则

来记录下MySQL在可重复读事务隔离级别下加锁的规则:

  1. 加锁的基本单位是next-key lock,它是前开后闭的原则。
  2. 查询过程中访问的对象会增加锁
  3. 索引上的等值查询–给唯一索引加锁的时候,next-key lock会升级为行锁
  4. 索引上的等值查询–向右遍历时,最后一个值不满足查询要求的话,会将next-key lock退化为间隙锁
  5. 唯一索引在范围查询时会访问到不满足条件的第一个值为止

通过案例解释上面的原则

先声明一下,下面的这些案例都是基于可重复读事务隔离级别的

数据介绍

假设现在我有几条数据:

步骤 id(唯一索引) c(普通索引) d(无索引)
1 1 1 1
2 5 5 5
3 10 10 10
4 15 15 15

以上数据为了解决幻读的问题,更新的时候,不只是对上述的每行数据都加行锁,并且对于中间的取值范围也加了锁,这个锁就是我们所说的间隙锁。这样以来,行锁+间隙锁组成next-key lock,这个锁的信息就是:(-°,1],(1,5],(5,10],(10,15],(15,+supernum],(其中,supernum是MySQL数据库维护的最大的值,来保证next-key lock是左开右闭的原则)

案例

案例1:间隙锁简单案例

步骤 事务A 事务B
1 begin;
select * from table where id = 3 for update
2 - insert into tabble value(4,4,4)
block;
3 commit; -

这个就是一个简单的案例,我们来分析下他的流程:

  • 当有上面这两个事务时,事务A的操作会对数据库增加一个(1,5]范围的锁,这时候事务B是不能进行插入数据的,处于阻塞状态

案例2:间隙锁死锁问题

步骤 事务A 事务B
1 begin;
select * from table where id = 3 for update
2 - begin;
select * from table where id = 4 for update
3 - insert into table value(2,2,2)
block;
3 nsert into table value(2,2,2)
block;
-

不同于写锁是互斥的,间隙锁不是互斥的,两个事务都能获取到这个间隙锁,但是获得这个间隙锁之后,是不允许其他事务对这个间隙锁进行DDL操作的,所以两者就进入到死锁状态

案例3:等值查询–唯一索引

步骤 事务A 事务B 事务C
1 begin;
update table set d=d+1 where id = 7
- -
2 - insert into table value(8,8,8)
block;
-
3 - - update table set d=d+1 where id=10

1、先来看看MySQL会给数据库生成什么范围的锁:(5,10]
2、我们根据上面的原则四,我们看到这个范围最后一个值10与我们要查的id=7不相等,就会将这个next-key lock退化为间隙锁(5,10);
3、所以就可以解释事务B进行的插入操作是阻塞的,而事务C的操作是不阻塞的了。

案例4:等值查询–普通索引

步骤 事务A 事务B 事务C
1 begin;
select id from table where c=5 lock in share mode
- -
2 - update table set d=d+1 where id=5 -
3 - - insert into table values(7,7,7)
block;

1、先来看看MySQL会给数据库生成什么范围的锁:(0,5],(5,10]
2、由于查询是等值查询,并且最后一个值不满足查询要求,故退化为间隙锁(5,10)
3、事务B能正常执行不阻塞的原因就是,事务A走的是覆盖索引并没有对主键索引加锁,所以B能正常执行
4、事务C插入的信息是7,在(5,10)之间,故会阻塞。

猜你喜欢

转载自blog.csdn.net/MarkusZhang/article/details/108379193