首页
学习
活动
专区
圈层
工具
发布
清单首页MySQL文章详情

RC隔离级别下的间隙锁案例

//

RC隔离级别下的间隙锁案例

//

MySQL在RR隔离级别下引入间隙锁来解决数据记录的幻读问题,在RC隔离级别下,通常间隙锁会消失,降级为记录锁,所以在RC隔离级别下能够提高并发写入的性能。

在某些特殊场景下,RC隔离级别也会包含间隙锁。来看这个案例:

首先我们创建一个表t3,t3只有1个字段id,id上面创建唯一索引,如下:

mysql> show create table t3\G ***************** 1. row **************** Table: t3 Create Table: CREATE TABLE `t3` ( `id` int(11) DEFAULT NULL, UNIQUE KEY `uniq_id` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec)

此时,在session A上执行delete操作,在session B和session C上执行insert操作,由于id是唯一索引,所以肯定会产生锁等待,如下:

要分析上面的加锁原理,首先需要下面2个知识点:

1、MySQL在唯一索引上加锁的原则:

唯一索引上的范围查询会访问到不满足条件的第一个值为止

这个加锁原则看似不太合理,像一个bug,因为唯一索引意味着所有的记录不能重复,理论上只需要添加记录的行锁就可以,但是实际中确实是需要访问到当前记录的下一条记录进行加锁。

2、插入意向锁

插入意向锁之间是不冲突的,插入意向锁也是一种间隙锁,他的存在是为了提高插入的并发度。在申请插入意向锁的时候,需要判断当前插入记录位置的下一条记录上是否持有锁,如果有,则需要判断是否与插入意向锁冲突。如果没有,则不需要判断,直接加上插入意向锁。

有了这两个概念,我们可以判断,在上面图示这个时刻,我们通过show engine innodb status命令查看当前状态下锁的情况,可以看到:

------- TRX HAS BEEN WAITING 7 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 232 page no 4 n bits 72 index uniq_id of table `yeyz`.`t3` trx id 295704 lock mode S waiting Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000001; asc ;; 1: len 6; hex 000000000605; asc ;;

可以看到,这个过程中,所以uniq_id上存在S类型的next_key锁,而next_key锁是由gap锁和记录锁组成的,也就是说出现了gap锁。

这里有必要将innodb中常见的锁种类说明一下:

可以看到lock mode S 代表的是next_key锁。

那么为什么innodb需要加S型的间隙锁呢?

如果采用S型的记录锁,当session A没有提交的时候,id=1这条记录上session A添加了x锁,并且session A需要做唯一性检查,当session A提交后,session B和session C同时获得S型的记录锁,并且与插入意向锁不冲突(因为插入的是1,意向锁的),由于session B和session C的S锁之间不冲突,所以session B和session C的插入都会成功。这和唯一索引的初衷违背,是我们不愿意看到的。

所以这里需要添加S类型的Next-key Lock来让插入意向锁发生冲突,从而实现锁冲突,阻止记录同时插入。

如果你使用的是MySQL8.0以上的版本,可以在performance_schema表中的data_locks中看到如下记录:

可以看到2个S的记录,从而证明其他两个session上添加的是S型的next_key锁。

下一篇
举报
领券