1、MVCC 与 Next-Key
-
MVCC不加锁,并发性高,读取到的不一定是实时数据
-
Next-Key需要加锁,获取到的是实时数据
-
InnoDB存储引擎 在 RR 级别(可重复读隔离级别) 下通过 MVCC和 Next-key Lock 来解决幻读问题:
-
在可重复读隔离级别下
“当前读”读取到的是数据库中最新数据
“快照读”读取到的是第一次建立ReadView时可见的数据(不一定是实时数据)
-
在可重复读事务隔离级别下,默认的查询语句是快照读,MVCC只是利用历史数据,在部分场景下规避了幻读,而要完全解决幻读,需要手动加锁将快照读调整为当前读(mysql不会自动加锁),即使用Next-Key来完全解决幻读
参考:https://blog.csdn.net/qq_27037443/article/details/94390952
在快照读下,即使读取的记录已被其它事务加上 X 锁,这时记录也是可以被读取的,即读取的快照数据。在 Repeatable Read 下 MVCC 解决了快照读下存在的幻读问题,只能读取到第一次查询之前所插入的数据(根据 Read View 判断数据可见性,Read View 在第一次查询时生成)。
在当前读下 ,每次读取的都是最新数据。这时如果两次查询中间有其它事务插入数据,就会产生幻读。所以, InnoDB 在实现Repeatable Read 时,如果执行的是当前读,则会对读取的记录使用 Next-key Lock ,来防止其它事务在间隙间插入数据
2、当前读、快照读
2.1 当前读(current read)
2.1.1 定义
当前读获得数据的最新版本。当前读会对读取到的记录加锁(除了第一个加共享锁S,其他都是互斥锁X):
select... lock in share mode(S锁):对记录加 S 锁,其它事务也可以加S锁,如果加 x 锁则会被阻塞
select... for update(X锁)、update(X锁)、insert(X锁)、delete(X锁):对记录加 X 锁,且其它事务不能加任何锁
在执行这几个操作时,会读取最新记录,即其它事务提交的数据也可以被查询到。比如要update一条记录,但是在另一个事务中已经delete掉这条数据并且commit了,如果update就会产生冲突,所以在update的时候需要知道最新的数据。
2.1.2 当前读下的幻读问题
在当前读下 ,每次读取的都是最新数据,但是,如果两次查询中间有其它事务在间隙插入数据,就会产生幻读(虽然当前读会加锁,但是加的X、S是行锁,可以避免修改某一行的数据,但是对于间隙无能为力)。所以, InnoDB 在实现Repeatable Read 时,如果执行的是当前读,则会对读取的记录使用 Next-key Lock ,来防止其它事务在间隙间插入数据
在当前读下,加Next-Key锁,阻塞其他并发事务,保证其它事务不会修改当前记录,读取到的是记录的最新版本,避免了当前读下的幻读
。
2.1.3 Next-Key锁解决当前读下的幻读
间隙锁
:所谓的Next-Key锁就是一个行锁(record lock)+范围锁(gap lock),只有在Read Repeatable、Serializable隔离级别才有Next-Key锁,其锁定一个范围并且锁定记录本身
。假设id有3,4,5,锁定id>3的数据时,4、5及后面的数字都会被锁定,这个锁一直到事务提交才会释放,若此时不锁定没有的数据,例如当加入了新的数据id=6,就会出现幻读,因此,间隙锁可以避免幻读。