快照读是如何避免幻读的(快照读 VS 当前读)
可重复读隔离级是由 MVCC(多版本并发控制)实现的,实现的方式是开始事务后(执行 begin 语句后),在执行第一个查询语句后,会创建一个 Read View,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,所以事务过程中每次查询的数据都是一样的,即使中途有其他事务插入了新纪录,是查询不出来这条数据的,所以就很好了避免幻读问题。
快照读 VS 当前读
这个例子本来我很疑惑,因为可重复读(repeatable read),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的吗,为什么图片中第二次select数据不应该是查询不到吗?
首先,我需要回顾可重复读的基本机制。可重复读通过MVCC和ReadView来保证事务内的快照读一致性。事务启动时生成ReadView,后续所有快照读都基于这个视图,避免不可重复读。但示例中的情况显示,事务A在更新后再次查询时看到了事务B插入的数据,这明显是幻读。
接下来,我需要分析示例中的具体操作。事务A第一次查询时没有找到id=5的记录,生成了ReadView。事务B插入并提交了id=5的记录。事务A执行更新操作,然后再次查询,结果看到了这条记录。这里的关键在于更新操作触发了当前读,导致数据版本的变化。用户可能混淆了快照读和当前读的区别。快照读使用ReadView,而当前读(如UPDATE)会读取最新提交的数据,并更新记录的trx_id为当前事务的ID。因此,当事务A更新后,该记录的trx_id被设为事务A的ID,后续快照读会认为这是自己的修改,从而可见。
一、核心区别
维度 | 快照读(Snapshot Read) | 当前读(Current Read) |
---|---|---|
数据版本 | 读取数据的历史版本(MVCC 维护的旧快照)。 | 读取数据的最新版本。 |
锁机制 | 无需加锁,非阻塞读。 | 对数据加锁(共享锁或排他锁),阻塞其他事务的写操作。 |
一致性视图 | 基于事务启动时的 ReadView,保证多次读取结果一致。 | 强制读取最新数据,保证实时性。 |
适用操作 | 普通 SELECT 语句。 | SELECT ... FOR UPDATE/LOCK IN SHARE MODE、INSERT/UPDATE/DELETE。 |
并发影响 | 无锁,高并发性能。 | 加锁可能引发锁竞争或死锁。 |
二、触发条件
- 快照读
- 触发场景:
在 可重复读(RR) 或 读已提交(RC) 隔离级别下执行普通 SELECT。
示例(来自图像):-- 事务A第一次查询(快照读) SELECT * FROM t_stu WHERE id=5;
- 触发场景:
结果:基于初始 ReadView 读取,未发现 id=5 的记录。
- 实现原理:
通过 ReadView 判断数据版本的可见性,从版本链中查找符合条件的历史版本。
- 当前读
- 触发场景:
显式加锁的查询:
数据修改操作(隐式触发当前读):SELECT * FROM t_stu WHERE id=5 FOR UPDATE;
影响:-- 事务A的 UPDATE 操作(当前读) UPDATE t_stu SET name='小林coding' WHERE id=5;
强制读取最新数据(发现事务B插入的 id=5 记录)。
更新后,该记录的 trx_id 被设为事务A的ID → 后续快照读可见(导致幻读)。
- 触发场景:
三、图像中的关键验证
- 快照读的局限性:
事务A首次快照读未发现 id=5 的记录(基于初始 ReadView)。
事务B插入并提交后,事务A的 UPDATE(当前读)强制读取最新数据,覆盖 trx_id。 - 当前读的副作用:
更新后的记录 trx_id=事务A的ID → 后续快照读可见(即使事务B已提交)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2021-02-25 dp+哈希