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

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

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

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

一 不可重复读 (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 隔离级别下,也是存在幻读问题的。

作为程序员,持续学习和充电非常重要,作为开发者,我们需要保持好奇心和学习热情,不断探索新的技术,只有这样,我们才能在这个快速发展的时代中立于不败之地。低代码也是一个值得我们深入探索的领域,让我们拭目以待,它将给前端世界带来怎样的变革,推荐一个低代码工具。

应用地址: https://www.jnpfsoft.com
开发语言:Java/.net

这是一个基于Flowable引擎(支持java、.NET),已支持MySQL、SqlServer、Oracle、PostgreSQL、DM(达梦)、 KingbaseES(人大金仓)6个数据库,支持私有化部署,前后端封装了上千个常用类,方便扩展,框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用。

至少包含表单建模、流程设计、报表可视化、代码生成器、系统管理、前端 UI 等组件,这种情况下我们避免了重复造轮子,已内置大量的成熟组件,选择合适的组件进行集成或二次开发复杂功能,即可自主开发一个属于自己的应用系统。

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PostgreSQL的MVCC(多版本并发控制)是一种用于支持事务并维持数据一致性的同时,允许并发读写的机制。这种技术在数据库管理系统中非常关键,尤其是在处理事务性和并发操作时。 ### MVCC的工作原理 #### 时间戳隔离 在MVCC中,每一行数据都有一个称为“时间戳”的版本标识符。当一个事务开始时,它会获取一个时间点作为其活动的开始,这个时间点包含了所有已提交事务的时间戳信息。事务在其整个生命周期内,只能够访问和修改那些在这个时间点前或同时创建的数据。 #### 数据行和快照 在MVCC中,数据行不是直接存储其原始值,而是包含了一个额外的版本信息。每个查询或操作都会从数据库中加载一组所谓的“快照”,即特定时刻的所有活跃事务的状态。这些快照由一系列的版本(版本树)组成,其中每个节点表示一个事务的更新状态。 #### 写入与删除 写入操作会在现有数据上建立一个新的版本,并将旧版本标记为历史记录。如果需要,可以保留多个历史版本供日志和恢复使用。删除操作实际上是修改数据的一次写操作,将其标记为不可见而非物理删除,以便于保持查询性能。 ### 相关优势 1. **高并发性**:通过限制事务对数据的访问范围,使得多个事务可以在同一时间内运行而不相互干扰。 2. **冲突解决**:自动管理并发操作带来的冲突,减少了应用开发者在并发处理上的复杂度。 3. **安全性**:避免了死锁和其他并发问题,提高了系统的稳定性和可靠性。 4. **性能优化**:通过避免全表扫描等低效操作,提高查询效率。 ### 使用场景及限制 1. **多用户环境**:在大量并发用户同时请求的情况下,MVCC能有效减少锁定等待时间和提高系统吞吐量。 2. **数据完整性**:确保在高负载下仍能满足ACID(原子性、一致性、隔离性、持久性)属性的需求。 3. **复杂查询**:支持复杂的SQL查询,包括JOIN操作,而不会因为并发读写引起的问题。 然而,尽管有上述优点,MVCC也有其局限性: - 需要更多的磁盘空间来存储额外的版本数据。 - 对一些特殊的查询或需求可能不如其他并发控制策略高效。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值