举例说明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;
posted @ 2023-10-07 17:17  进击的小蔡鸟  阅读(72)  评论(0编辑  收藏  举报