一、概览
MVCC在一定程度可以避免幻读,但是不能完全解决幻读
二、举例说明什么是幻读,重现幻读现象
什么是幻读
把读写事务出现的三个问题再回顾一下
脏读:事务A读取到事务B未提交的数据,此时为脏读。
不可重复读:在事务A中先后两次读取同一个数据,两次读取的结果不一样,这种现象称为不可重复读。
幻读:事务A以同种条件先后查询数据库,两次查询结果的条数不同,这种现象称为幻读。
脏读和不可重复读的区别:前者读到的是其他事务未提交的数据,后者读到的是其他事务已提交的数据
不可重复读与幻读的区别:前者是数据变了,后者是数据的行数变了。
举例说明幻读
首先设置事务A为读已提交隔离级别。
三、举例说明可以一定程度上解决幻读
现在设置事务A的隔离级别为可重复读
前面我们将MVCC实现可重复读原理中提到,读分为快照读和当前读(或者叫加锁读)。当前读就是写操作需要先读取最新的数据然后再写,写的过程是加锁的。
列举下快照读和当前读的操作。
1. 快照读(snapshot read)
简单的select操作(不包括 select ... lock in share mode, select ... for update)
2.当前读(current read)
select ... lock in share mode
select ... for update
insert
update
delete
因此在可重复读的时候,读的是快照中的数据,即ReadView中的数据,也就是历史数据。
那么为什么读已提交会产生幻读现象呢?
因为在RC(读已提交)隔离级别下,是每个快照读都会生成并获取最新的Read View;而在RR(可重复读)隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。
四、举例说明没有完全解决幻读
RR虽然避免了幻读问题,但是毕竟不是Serializable,不能保证完全的隔离
如果在事务中第一次读取采用快照读,第二次读取采用当前读,则如果在两次读取之间数据发生了变化,两次读取到的结果不一样,因为加锁读时不会采用MVCC。如下例子
所以从以上实验中就可以得出MVCC在一定程度可以避免幻读,但是不能完全解决幻读