在MySQL中的锁机制文章中我们讲解了MySQL中涉及的锁相关概念,本片文章我们来详细解析下MySQL中的间隙锁,跟着我的脚步一起学习吧!有问题欢迎大家指正讨论!😄😄😄
间隙锁用来解决什么问题?
用来解决幻读问题,并且幻读只在RR隔离级别下才会存在。
什么是幻读?
幻读指的是同一个事务前后两次查询同一个范围的时候,后一次查询看到了前一次查询没看到的行。
说明:
- 幻读仅专指“新插入的行”
- 在RR隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的,因此,幻读在“当前读”下才会出现
幻读导致的问题
例:
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `c` (`c`)
) ENGINE=InnoDB;
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);
| sessionA | sessionB | sessionC |
|---|---|---|
| begin; select * from t where d=5 for update; update t set d=100 where d=5; | ||
| update t set d=5 where id=10;(由于sessionA获取的是id=5这一行的行锁,因此更新id=10这一行记录不会block) | ||
| insert into t values(6,6,5);(行锁对更新没用) | ||
| commit; |
由于update是当前读,在commit的时候会将update语句写入binlog,那binlog中记录的内容就是
update t set d=5 where id=10;//sessionB
insert into t values(6,6,5);//sessionC
update t set d=100 where d=5;//sessionA
那在从库重放binlog的时候就会将id=10、id=6这两行记录的d字段更新为100,而主库中的记录id=10的d字段为5,id=6的d字段也为5,出现了主从数据不一致的情况。
**为了解决幻读的问题,因此InnoDB引入的间隙锁,**产生幻读的原因是行锁只能锁住行,而对于要操作记录间隙的操作(insert就是),InnoDB就只好引入间隙锁来解决。
比如上边的例子,就会加7个间隙锁:(-∞ ,0)、(0,5)、(5,10)、(10,15)、(15,20)、(20,25)、(25,+supremum)。InnoDB 给每个索引加了一个不存在的最大值 supremum。
间隙锁之间不冲突。
next-key lock
间隙锁和行锁合称next-key lock,每个nextkey lock都是前开后闭区间。
间隙锁加锁规则
- 对主键或唯一索引,如果当前读时,where条件全部精确命中(=或者in),这种场景本身就不会出现幻读,所以只会加行记录锁。
- 非索引的列,当前读操作时,会加全表gap锁,生产环境要注意。
- 非唯一索引列,如果where条件部分命中(>、<、like等)或者全未命中,则会加附近Gap间隙锁。例如,某表数据如下,非唯一索引2,6,9,9,11,15。如下语句要操作非唯一索引列9的数据,gap锁将会锁定的列是(6,11],该区间内无法插入数据。
间隙锁和next-key lock带来的问题
导致锁范围变大,可能导致死锁。
问题复现:任意锁住一行,如果这一行不存在,就插入,如果存在就更新,当出现并发时,就会发生死锁。
| sessionA | sessionB |
|---|---|
| begin; select * from t where id=9 for update;(id=9这行记录不存在,加上间隙锁(5,10]) | |
| begin; select * from t where id=9 for update ;(id=9这行记录不存在,加上间隙锁(5,10]) | |
| insert into t values(9,9,9);(blocked)(被sessionA的间隙锁阻塞) | |
| insert into t values(9,9,9);(blocked)(被sessionB的间隙锁阻塞) |
上面这种情况就发生死锁了,说明间隙锁的引入可能会导致同样的语句锁住更大的范围,影响了并发度。
如何避免间隙锁带来的问题
不使用可重复度隔离级别,但是为了解决可能出现的数据和日志的不一致情况,需要把binlog的格式改为row。
本文深入探讨了MySQL中的间隙锁(Gap Locks),其主要目的是防止幻读现象,尤其在可重复读(Repeatable Read)隔离级别下。间隙锁通过锁定索引间隙来阻止新插入的行,但可能增加锁的范围,引发死锁。理解间隙锁的工作原理和影响对于优化并发性能和避免数据不一致至关重要。同时,文章还讨论了Next-Key Locks、加锁规则以及如何避免间隙锁带来的问题。
5876

被折叠的 条评论
为什么被折叠?



