MySQL中的幻读

一、什么是幻读

1.我们先来回顾一下MySQL中事务隔离级别

  • READ UNCOMMITTED :未提交读。
  • READ COMMITTED :已提交读。
  • REPEATABLE READ :可重复读。
  • SERIALIZABLE :可串行化。

2.针对不同的隔离级别,并发事务可以发生不同严重程度的问题

  • READ UNCOMMITTED 隔离级别下,可能发生脏读不可重复读幻读问题。
  • READ COMMITTED 隔离级别下,可能发生不可重复读幻读问题,但是不会发生脏读问题。
  • REPEATABLE READ 隔离级别下,可能发生幻读问题,但是不会发生脏读不可重复读的问题。
  • SERIALIZABLE 隔离级别下,各种问题都不会发生。

3.MySQL的默认隔离级别是REPEATABLE READ,可能会产生的问题是幻读,也就是我们本次要讲内容。

首先来看看 MySQL 文档是怎么定义幻读(Phantom Read)的:

当同一个查询在不同的时间产生不同的结果集时,事务中就会出现所谓的幻象问题。
例如,如果 SELECT 执行了两次,但第二次返回了第一次没有返回的行,则该行是“幻像”行。

如图所示
在这里插入图片描述

二、可重复读是如何避免幻读的

快照读情况下

在可重复读隔离级别下是通过MVCC来避免幻读的,具体的实现方式在事务开启后的第一条select语句生成一张Read View(数据库系统当前的一个快照),之后的每一次快照读都会读取这个Read View。
在这里插入图片描述
即在第②时刻生成一张Read View,所以在第⑤时刻时读取到数据和第②时刻相同,避免了幻读。

当前读情况下

当前读:像select lock in share mode(共享锁), select for update ; update, insert ,delete这些操作都是一种当前读,读取的是记录的最新版本。
在当前读情况下是通过next-key lock来避免幻读的,即加锁阻塞其他事务的当前读。在这里插入图片描述
事务A在第②时刻执行了select for update当前读,会对id=1和2加记录锁,以及(2,+∞)这个区间加间隙锁,两个都是排它锁,会阻塞其他事务的当前读,所以在第③时刻事务B更新时阻塞了,从而避免了当前读情况下的幻读。

三、可重复读完全解决幻读了吗?

MySQL的默认隔离级别可重复能避免大部分情况下的幻读,但是在一些特殊场景下还是无法完全解决幻读。例如
在这里插入图片描述
在第②时刻使用的是快照读,此时生成了Read View查询出来的数据是张三、李四,事务B在第③时刻插入了一条id为3的王二,因为事务A并没有对数据加锁,所以事务B可以正常插入。但当第⑤时刻事务A查询时却查出来了事务B插入的数据,产生了幻读。是因为使用的是当前读,不会读取Read View,是读取数据当前最新的数据,所以读出了事务B插入的数据。

四、总结

MySQL的默认隔离级别可重复读很大程度上解决了幻读问题。在快照读情况下是通过MVCC解决的,在第一次执行查询语
句时生成一张Read View,后续每次快照读都是读这张Read View。在当前读情况下是加锁来解决的,会阻塞其他事务的
当前读,从而避免幻读。然而可重复读并不能完全解决幻读,当一个事务里面使用快照读之后又使用当前读的话就还是
会出现幻读。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ByLir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值