MySQL中的间隙锁

本文深入探讨了MySQL中的间隙锁(Gap Locks),其主要目的是防止幻读现象,尤其在可重复读(Repeatable Read)隔离级别下。间隙锁通过锁定索引间隙来阻止新插入的行,但可能增加锁的范围,引发死锁。理解间隙锁的工作原理和影响对于优化并发性能和避免数据不一致至关重要。同时,文章还讨论了Next-Key Locks、加锁规则以及如何避免间隙锁带来的问题。


MySQL中的锁机制文章中我们讲解了MySQL中涉及的锁相关概念,本片文章我们来详细解析下MySQL中的间隙锁,跟着我的脚步一起学习吧!有问题欢迎大家指正讨论!😄😄😄

间隙锁用来解决什么问题?

用来解决幻读问题,并且幻读只在RR隔离级别下才会存在。

什么是幻读?

幻读指的是同一个事务前后两次查询同一个范围的时候,后一次查询看到了前一次查询没看到的行。

说明:

  1. 幻读仅专指“新插入的行”
  2. 在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);
sessionAsessionBsessionC
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都是前开后闭区间。

间隙锁加锁规则

  1. 对主键或唯一索引,如果当前读时,where条件全部精确命中(=或者in),这种场景本身就不会出现幻读,所以只会加行记录锁。
  2. 非索引的列,当前读操作时,会加全表gap锁,生产环境要注意
  3. 非唯一索引列,如果where条件部分命中(>、<、like等)或者全未命中,则会加附近Gap间隙锁。例如,某表数据如下,非唯一索引2,6,9,9,11,15。如下语句要操作非唯一索引列9的数据,gap锁将会锁定的列是(6,11],该区间内无法插入数据。

间隙锁和next-key lock带来的问题

导致锁范围变大,可能导致死锁。

问题复现:任意锁住一行,如果这一行不存在,就插入,如果存在就更新,当出现并发时,就会发生死锁。

sessionAsessionB
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。




评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

壹氿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值