文章目录
前言
提示:演示间隙锁是如何工作的,以及如何解决幻读的
一、锁的算法
1. 三种算法
- Record Lock (读已提交用的是这个)
- Gap Lock
- Next-Key Lock : Record Lock + Gap Lock (可重复读用的是这个)
2. Next-Key Lock锁住的范围
假设一个索引有10,11,13,20 这四个值,那么锁住的范围如下:
- (负无穷,10]
- (10,11]
- (11,13]
- (13,20]
- (20,正无穷]
二、示例 (可重复读隔离级别)
1.当查询的索引有唯一属性时
1. 准备数据
代码如下(示例):
create table lock_next_key_unikey(a int primary key);
insert into lock_next_key_unikey (a) values(1),(2),(5);
2.演示
A事务:
// 开启事务
begin ;
// 对 a = 5 上X锁
select * from lock_next_key_unikey where a = 5 for update ;
B事务:
// 开启事务
begin ;
// 插入 a = 4 (这个会成功)
insert into lock_next_key_unikey (a) values(4);
说明: 之所以会插入成功的原因是:当查询的索引含有唯一属性时,如果是联合唯一索引,需要全部查询,则Next-Key Lock降级为行锁,仅锁住了a=5这一行。不影响其他行的读写。
2.当查询的索引没有唯一属性时
1. 准备数据
代码如下(示例):
create table lock_next_key_key(a int , b int ,PRIMARY key(a),key(b));
insert into lock_next_key_key (a,b) values (1,1),(3,1),(5,3),(7,6),(10,8);
2.演示
1.锁住这行
加锁都是对索引加的。
A事务:
// 开启事务
begin ;
// 对 b = 3 上X锁
select * from lock_next_key_key where b = 3 for update ;
而如果是
select * from lock_next_key_key where b = 1 for update ;
则会锁住多行。
B事务:
// 开启事务
begin ;
// 获取a=5这行数据的S锁 , 因为A对它有X锁,故等待
select * from lock_next_key_key where a = 5 lock in share mode ;
C事务:
// 开启事务
begin ;
// 插入数据 (阻塞)
insert into lock_next_key_key (a,b) values(4,2);
insert into lock_next_key_key (a,b) values(6,5);
说明: 这两个插入都会被阻塞,因为:当查询的是辅助索引,且不是唯一索引时,会锁住前后两个区间,这里锁住的辅助索引对应的区间是(1,3),(3,6),当然b=3这个也是会被锁住的,因此b=2,5都在这个被锁住的区间,因此插入是阻塞的。
3. 锁住的是聚集索引和辅助索引对应的行和范围
与查询里面是否包含什么无关,与涉及到的行有关,我们增加一列:
alter table lock_next_key_key add column c int ;
update lock_next_key_key set c = b ;
然后开启一个事务,并
select * from lock_next_key_key where c = 3 for update ;
则效果和
select * from lock_next_key_key where b = 3 for update ;
是一样的,锁住的都是行对应的索引的对应的索引区间。