我们知道在RR可重复读事务隔离级别下,是存在幻读问题的。但是在《高性能Mysql》中看到说mysql通过MVCC(多版本并发控制)解决了幻读问题的。所以就查了下资料,决定通过实践来验证一下这个问题。
实践是检验真理的唯一标准!这里先说结论:Mysql在可重复读事务隔离级别下,很大程度上避免了幻读,但某些场景下仍然是存在幻读问题的;可以通过MVCC或记录锁+间隙锁解决幻读问题。
验证过程:
- 先查看下当前数据库的事务隔离级别,确认为REPEATABLE-READ级别。
- 创建测试表
CREATE TABLE `test` (
`id` int(8) NOT NULL AUTO_INCREMENT,
`NAME` varchar(16) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 打开两个连接窗口,开启两个事务进行测试
我们先插入一条数据: insert test(name) values('aaa');
场景1:
事务1先执行一次查询,返回1条数据;
然后开启事务2,执行 insert test(name) values('aaa'); commit 提交事务2;
事务1再次查询,是查询不到事务2新增的数据的。
结论:该场景下没有产生幻读(快照读,通过MVCC方式解决了幻读)。
场景2:
接场景1(上图),在事务1中执行
update test set name='ccc' where name='aaa';
奇怪的情况出现了,可以看到前面查询时表里只有一条数据,但是执行update时却更新了两条数据,再次查询,查询到两条数据。
结论:该场景下出现幻读。
可以使用当前读(select for update)来避免这种幻读现象(当前读,通过记录锁+间隙锁的方式解决幻读);
或者在表中增加version字段,在select的where条件中手动控制version来避免幻读。
总结:
- 场景1 insert的场景下,mysql通过MVCC的方式进行快照读,实现读已提交和可重复读。不会出现幻读情况。快照读,通过MVCC方式解决了幻读。
- 但是在场景2 update的场景下,是会出现幻读的。可以使用当前读(select for update)来避免这种幻读现象(当前读,通过记录锁+间隙锁的方式解决幻读);或者在表中增加version字段,在where条件中手动控制version来避免幻读。
参考: