举例说明MVCC没有完全解决幻读
参考资料地址1
: MySQL(五)—MVCC解决可重复读的幻读
参考资料地址2:
MySQL到底有没有解决幻读问题?这篇文章彻底给你解答
参考资料地址3:
Mysql解决幻读
常见问题
- 脏读: 一个事务读到其他事务未提交的数据。
- 不可重复读: 一个事务读取到其他事务修改过的数据。
- 幻读: 一个事务读取到其他事务最新插入的数据
举例说明没有完全解决幻读
RR虽然避免了幻读问题,但是毕竟不是Serializable,不能保证完全的隔离
示例
如果在事务中第一次读取采用快照读,第二次读取采用当前读,则如果在两次读取之间数据发生了变化,两次读取到的结果不一样,因为加锁读时不会采用MVCC。如下例子
时刻 | 事务A | 事务B |
---|---|---|
T1 | begin; | begin; |
T2 | SELECT * FROM customer ; |
|
T3 | INSERT INTO hibernate .customer (id , password , username ) VALUES (3, '288', '4545');commit; |
|
T4 | SELECT * FROM customer ; |
|
T5 | SELECT * FROM customer for update; |
T4 看不到事务B新插入的数据
T5 可以看到新插入的数据(幻读)
所以从以上实验中就可以得出MVCC在一定程度可以避免幻读,但是不能完全解决幻读
for update的当前读,出现了幻读问题,其他的当前读也会复现幻读问题,如insert。
原因
原因是,在可重复读隔离级别下,每次执行当前读会生成一个新的读视图,所以能读到其他事务最新插入的数据。
测试
当前读后再次执行快照读,看到的数据是正常的数据(没有幻读,read_view视图应该是第一次快照读生成的)
解决方案
要避免这种情况出现,要么就不要混用快照读和当前读,要么就在事务开启的时候马上执行select ... for update 这类当前读的语句,因为它会对记录加next-key lock,从而避免其他事务插入一条新的纪录。
Next-key lock: 就是记录锁和间隙锁的组合
ps:
CREATE TABLE `customer` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`password` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;