写点什么

不可重复读和幻读有什么区别

作者:高端章鱼哥
  • 2024-09-12
    福建
  • 本文字数:1910 字

    阅读完需:约 6 分钟

不可重复读和幻读有什么区别

也不是啥难题,但是上周确确实实有两个简历上八年经验的人没答出来(这两个八年经验的小伙伴,一个是资深一个是高级)。

不过松哥的读者藏龙卧虎,相信在座的各位回答这道题没什么压力。

题目就是:不可重复读和幻读有什么区别

一 不可重复读 (Non-Repeatable Read)

不可重复读是指在一个事务内多次读取同一数据的时候,由于其他事务对这些数据进行了修改并提交,导致读取的结果不一致。换句话说,在同一个事务中,如果两次读取之间有另一个事务修改了数据并提交,那么第一次读取和第二次读取可能会得到不同的结果。

举个简单例子:

  1. 事务 A 读取行 x = 100。

  2. 另一个事务 B 更新行 x 为 200 并提交。

  3. 事务 A 再次读取行 x,发现其值变为 200。

在这种情况下,事务 A 第一次读取到的数据与第二次读取到的数据不一致,即使两次读取操作都在同一个事务中进行。

这就是不可重复读,可以看到,不可重复读强调的是同一条数据被修改。

二 幻读 (Phantom Read)

幻读是指在一个事务内多次执行相同的查询时,由于其他事务插入或删除了一些记录,导致返回的记录数量或具体内容发生变化的现象。这种现象之所以被称为“幻读”,是因为原本不存在的记录似乎突然出现(或者消失),就像幽灵一样。

举个简单的例子:

  1. 事务 A 执行 SELECT * FROM table WHERE id > 5,返回 n 条记录。

  2. 另一个事务 B 插入了一条 id=6 的记录并提交。

  3. 事务 A 再次执行相同的查询,这次返回 n+1 条记录。

在这种情况下,事务 A 第一次查询到的记录数与第二次查询到的记录数不同,即使两次查询操作都在同一个事务中进行。

总结下就是:

不可重复读和幻读都是在同一个事务中执行相同的 SQL 多次,结果不同。在这个过程中,不可重复读说的是读取的记录被 update,而幻读则强调读取的记录中出现了 insert 或者 delete 操作。

三 连环追问

3.1 不可重复读和幻读分别是如何解决的

这里就涉及到两个关键的概念:MVCC 和 Gap Lock。

MVCC 和 Gap Lock 松哥之前都有专门的文章和大家聊过,这里就不啰嗦了,就和大家捋一捋思路。

MVCC 的核心思路就是在事务启动的一瞬间,给数据库表中的数据拍一张照片,接下来在整个事务执行期间,查询的数据都是从这张照片上查询,这样即使有其他的事务对表中的数据做了修改,也查不到。这样就解决了不可重复读问题了。

从表面上看,MVCC 似乎解决了幻读问题了,因为在当前事务执行期间,别的事务对表的修改都是不可见的,也就是别的事务插入或者删除了数据,当前事务也是不可见的。要从这个角度看,MVCC 确实在一定程度解决了幻读。

但是,这种 MVCC 解决幻读的方式更类似于一种“掩耳盗铃”的方式,因为别的事务还是可以插入和删除,只不过你自己看不见而已。

这块有的小伙伴可能会有疑问,数据修改了不可见就没问题,为啥插入和删除不可见就不行?这里涉及到一个锁的问题。给大家举个简单的案例:

现在 A 事务执行如下 SQL:

update t1 set age=age+1 where id>5;
复制代码

假设表中有 id 为 6、7、8 的记录,这个例子中,在事务提交之前,按理说 id>5 的记录都会被锁定,也就是 id 为 6、7、8 的记录会被锁定,锁定了就不能操作了。但是,你会发现能够插入 id 为 6 的记录,原因在于 id 为 6 的记录锁定了,但是 6 和 7 之间有缝隙,这个缝隙没锁定,所以可以往缝隙里插入数据,既然可以插入,也就可以删除,这边就造成 id 为 6 的记录似乎没锁定的观感。

当然,实际上这个问题是不存在的,因为 MySQL 中有一个 Gap Lock 就是用来解决这个问题的。

Gap Lock,也就是间隙锁,利用间隙锁将被操作记录两端的缝隙都锁住,这样就直接阻止了其他事务执行 insert 或者 delete 了。

因此,Gap Lock 和 MVCC 实际上互为补充,MVCC 解决了不可重复读问题,同时 MVCC 也解决了快照读的幻读问题,Gap Lock 则从源头上禁止插入和删除,来进一步辅助 MVCC 去解决幻读问题。

当然,说到 Gap Lock,往往还有两个相关的概念,Record Lock 和 Next-Key Lock,这块我就不展开了,不了解的小伙伴可以看下松哥之前的文章,一般来说这几个锁会连在一起提问,包括锁的退化问题都会在这里问到。

3.2 可重复读隔离级别是否存在幻读

有的人可能会歪打正着的把这个问题回答对。

按理说,MVCC+Gap Lock 已经解决了幻读问题了,所以一般理解 MySQL 默认的可重复读隔离级别就不存在幻读问题了。

没错,在可重复读隔离级别下,大部分的幻读都被解决了,但是也有例外。

例如:

  1. 如果两个事务,事务 A 先进行快照读,然后事务 B 插入了一条记录并提交,再在事务 A 中进行 update 新插入的这条记录,发现可以更新,这就是发生了幻读。

  2. 事务 A 先进行快照读,然后事务 B 插入了一条记录并提交,在事务 A 中进行了当前读之后,再进行快照读也会发生幻读。

所以,在 MySQL 的 RR 隔离级别下,也是存在幻读问题的。

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
不可重复读和幻读有什么区别_高端章鱼哥_InfoQ写作社区