欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-14 16:56:04
...

前言

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有三种行锁的算法:
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] 即 当前行+两边的间隙 (1,3],(3,5],
这些范围的行数据和索引都被锁住,所以可以防止insert或者delete 带来的幻读

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