当前位置: 首页 > 工具软件 > next-update > 使用案例 >

MySQL的快照读(MVCC)和当前读(行锁、间隙锁、Next-Key Lock)解决幻读

诸葛皓
2023-12-01

前言

MySQL的innoDB引擎虽然拥有标准的四级隔离级别,不过它有其他数据库不同,就是在可重复读 RR 级别下面已经可以防止幻读的发生。所谓幻读,指的是事务A执行过程中,由于事务B并发插入删除了多条新数据,事务A两次读数据的行数不一样,出现了“虚幻”的新纪录(phantom,幽灵)。

一、快照读(snapshot read)

在快照读(snapshot read)的情况下,MySQL通过MVCC(多版本并发控制)来避免幻读。

原理:将历史数据存一份快照,所以其他事务增加与删除数据,对于当前事务来说是不可见的。

快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。主要应用于无需加锁的普通查询(select)操作。

快照读sql语句如下

select * from table where ?;

二、当前读(current read)

在当前读(current read)的情况下,当前读,读取的是记录的最新版本,并且会对当前记录加锁,防止其他事务发修改这条记录。MySQL通过next-key lock来避免幻读。

当前读sql语句如下:

select * from table where ? lock in share mode; 
select * from table where ? for update; 
insert; 
update; 
delete;

InnoDB有三种行锁的算法,都是索引上的锁,需要开RR隔离级别:
1、Record Lock 行锁:单个行记录(索引记录)上的锁。
1、Gap Lock 间隙所:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
1、Next-Key Lock1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。

因为InnoDB对于当前读 行的查询都是采用了Next-Key Lock的算法,锁定的不是单个值,而是一个范围(GAP)。
假设索引a有1,3,5,8,11,其记录的GAP的区间如下:是一个左开右闭的空间(原因是默认主键的有序自增的特性,结合后面的例子说明)

(-∞,1],(1,3],(3,5],(5,8],(8,11],(11,+∞)

select * from t where a = 3 for update;

如果是辅助索引?那么锁住的范围有 (1,3)3,(3,5] 即 当前行+两边的间隙 next-key范围(1,3],gap范围(3,5],锁住范围根据索引类型变换
这些范围的行数据和索引都被锁住,所以可以防止insert或者delete 带来的幻读

但是注意,对于可重复读默认使用的就是next key lock,但是对于“唯一索引” ,比如主键的索引,next key lock会降级成行锁Record Lock 即仅锁住索引本身,而不会锁住一个区间。

如果没有索引,上述UPDATE语句将会锁住表格中的每一条记录和每个间隙,其它UPDATE、INSERT和DELETE语句都无法执行,相当于锁表+锁所有的间隙

 类似资料: