InnoDB中的repeatable read这种隔离级别通过间隙锁+MVCC解决了大部分的幻读问题,但是并不是所有的幻读都能解读,想要彻底解决幻读,需要使用Serializable的隔离级别。
MVCC解决幻读
在RC中,每次读取都会重新生成一个快照,总是读取行的最新版本。在RR中,快照会在事务中第一次SELECT语句执行时生成,只有在本事务中对数据进行更改才会更新快照。
有下面执行过程:
由于RR下一个事务中只会生成一个readview,所以事务1第二次select时并没有查询到事务2插入进来的数据。解决了幻读。
间隙锁解决幻读
加锁的select或update后会更新当前快照:
select * from table lock in share mode;
select * form table for update
insert into table ...
update table ...
这个例子中,事务1select时用了间隙锁锁上了10到30之间的数据,所以事务2插入时会被阻塞,直到事务1commit或rollback后。
没有完全解决幻读
如果第一个例子中是加锁的select,那么则会查到事务2提交的数据,即发生了幻读。或者下面的例子也是:
事务2插入后,事务1两次select,第二次select到了事务2新插入的数据,即又发生了幻读!
或者再下面这个例子,也是发生了幻读:
在RR中,如果本事务中,执行了加锁的select或更新数据后,都会更新快照。
怎么避免幻读
如果想要彻底解决幻读的问题,在InnoDB中只能使用Serializablei串行执行这种隔离级别。但是,实际场景中,几乎很少连幻读都接受不了的场景,甚至不少大厂为了并发度都把RR改成RC。