
GAP Lock只会出现在RR隔离级别下!
GAP Lock只会出现在RR隔离级别下!
GAP Lock只会出现在RR隔离级别下!
mysql> show create table user;+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| Table | Create Table |+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| user | CREATE TABLE `user` (`id` int NOT NULL,`score` int DEFAULT NULL,KEY `socre` (`score`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci |+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+1 row in set (0.00 sec)当前表结构,只有一个普通索引score。我们下面的测试都是在普通索引score的基础上完成的。


从上图可以发现,插入score为10、15、25都插入失败,score为30的时候插入成功。原因是SessionA的select操作,不止锁了score为20的行。
从数据库的记录和索引字段,我们可以分析,这个表的间隙被分成了下面的情况:
(negative infinity,10)
(10,20),
(20,30),
(30,40)
(40,positive infinity)
而RR隔离级别,默认是使用Next-key Lock,Next-Key Lock加锁有下面几个规则。
Next-Key Lock是前开后闭区间。
查找过程中访问到的对象才会加锁。
索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为record lock。
索引上的等值查询,向右遍历时且最后一个值不满足等值条件时,next-key lock退化为gap lock。
分析上图的两个事务的操作,在A事务加锁之后,实际的加锁范围是[10,30),sql查询流程如下:
通过score索引,找到第一个值,score=10,不满足条件,继续向下寻找。
找下一个节点score=20,符合条件,会加上行锁和间隙锁。此时的加锁状态是(10,20];但是根据规则2,查找过程访问到的上一个记录score=10,所以会将10也锁住,加锁范围变成[10,20]。
score不是唯一索引,所以会继续向下寻找,下一个节点为30,满足规则2,所以会加上(20,30],但是30不满足等值查询score=20,所以根据第4条规则next-key lock退化为gap lock(20,30)。
至此,事务加锁结束,最终的加锁范围是[10,30)
mysql> show create table user;+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| Table | Create Table |+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| user | CREATE TABLE `user` (`id` int NOT NULL,`score` int DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `socre` (`score`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci |+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+1 row in set (0.00 sec)

唯一索引情况,查询的结果包含想要查询的记录,不会产生间隙锁。


唯一索引情况,查询结果不包含想要的记录,会产生间隙锁。

唯一索引下,如果查询结果不包含想要的记录,会产生间隙锁。从上图和Next-key Lock加锁规则我们可以得出,上图Session A操作之后,产生的锁范围是[6,10),不过因为是唯一索引,所以查询id为6或id为10的时候报错不是提示等待超时,而是直接提示重复。

Gap Lock加锁总结

间隙锁锁定的是索引记录之前和之后的一个间隙范围。
可以对同一个间隙重复加间隙锁。
间隙锁可能造成死锁。
间隙锁是RR隔离级别下的。
间隙锁只影响一般索引,对于唯一索引或者主键,如果查询的结果包含这个记录,那么另外的会话插入该记录前后,不会产生间隙锁;如果查询结果不包含这个记录,另外的会话插入该记录前后的间隙,会产生间隙锁。
END
参考:
http://guitoo.cc/wordpress/2019/05/07/
https://segmentfault.com/a/1190000013307132




