首先介绍一下当前读与快照读的区别,当前读就是像共享锁, 排他锁这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。而快照读就是不加锁的非阻塞读,快照读的实现是基于多版本并发控制,即 MVCC。既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。所以说MVCC 多版本并发控制是 「维持一个数据的多个版本,使得读写操作没有冲突」 的概念。
先上个例子:
假设以上隔离级别是RR,在RR隔离级别下,手动启动一个事务,进行select操作,他会生成一个readview模板(也就是快照读),可以理解为将当前数据库的数据复制一份,在当前事务中,之后不管进行多少次select查询,都是在模板中去取数据,所以不管数据库中是否对数据进行了改变,都不会影响当前事务数据的读取,从而避免了幻读。(也就是对应上图中的步骤1-6)
但是如果在当前事务中使用了类似下图语句进行当前读就会出现幻读:
也就是对应图中的步骤7。为什么呢?
因为for update是进行当前读的操作,他会重新从数据库去加载当前的最新的数据,每执行一次加载一次,如果在此时,另外一个事务为数据库添加了一个事务,再进行查询,会发现查询的数据与之前相比多了或者少了,这也就是幻读现象。
对于上述问题的解决方法:为当前读加上了 Next-key Lock,也就是间隙锁和行锁的统称,行锁防止了别的事务修改或者删除,间隙锁防止了别的事务新增。也就是在进行上面的for update事务中,其他的事务不能对数据进行增删操作,否则执行会报错。



