MySQL 的 MVCC 到底解决了幻读问题没有?请举例说明。

MySQL 的 MVCC(多版本并发控制)并未完全解决幻读问题,仅在特定隔离级别(Repeatable Read,可重复读)下缓解了部分幻读场景,无法处理"当前读"引发的幻读。

先说结论

① mysql可重复读不能完全避免幻读

② 幻读的出现,往往是在一个事务里面混用了快照读和当前读。

③ 想彻底避免幻读,就在事务一开始,把普通的select 换成 select ... for update 直接加锁,把这个范围锁住,别的数据就无法进行数据的插入干扰了。

1. 先明确:MVCC 如何"缓解"幻读(快照读场景)

InnoDB 的 MVCC 核心是通过"快照读"(如 SELECT * FROM table)读取事务启动时的数据快照,避免读取其他事务新增/删除的数据,从而在 Repeatable Read 级别下实现"重复读",间接减少幻读。

示例(快照读无幻读):

假设表 user 初始数据:id=1, name='A',当前隔离级别为 Repeatable Read。

事务1(快照读):

1.启动事务,执行 SELECT * FROM user WHERE id > 0,结果为 [id=1](读取的是事务启动时的快照)。

2.此时事务2启动,执行 INSERT INTO user (id, name) VALUES (2, 'B') 并提交。

3.事务1 再次执行 SELECT * FROM user WHERE id > 0,结果仍为 [id=1](未读取到事务2新增的 id=2)。

此场景下,MVCC 通过快照读避免了"第二次查询出现新数据"的幻读,因为事务1始终读的是自己启动时的快照。

2. MVCC 无法解决的幻读(当前读场景)

"当前读"(如 SELECT ... FOR UPDATE、UPDATE、DELETE)会读取最新的数据库数据(而非快照),此时若其他事务已新增符合条件的数据,会导致"当前读"读到新数据,产生幻读------这是 MVCC 无法解决的。

示例(当前读仍有幻读):

沿用上述表 user 初始数据,隔离级别仍为 Repeatable Read。

事务1(包含当前读):

1.启动事务,执行 SELECT * FROM user WHERE id > 0(快照读),结果为 [id=1]。

2.事务2启动,执行 INSERT INTO user (id, name) VALUES (2, 'B') 并提交。

3.事务1 执行 SELECT * FROM user WHERE id > 0 FOR UPDATE(当前读,锁定数据),结果为 [id=1, id=2](读到了事务2新增的数据)。

此场景下,事务1的"当前读"出现了"第一次没读到、第二次读到"的幻读,MVCC 无法避免------因为当前读的逻辑是"读最新数据",而非快照。

结论

  • MVCC 仅在 Repeatable Read 级别下的"快照读"场景 中,通过数据快照避免了幻读

  • 对于 "当前读"场景(如加锁查询、更新/删除),MVCC 无法解决幻读,需通过更高隔离级别(如 Serializable,串行化)或手动加表锁来彻底避免

相关推荐
我是华为OD~HR~栗栗呀3 小时前
华为od-22届考研-测试面经
java·c++·python·功能测试·华为od·华为·面试
Warren984 小时前
复习MySQL
数据库·windows·tcp/ip·mysql·ubuntu·ssh·ansible
凌~风4 小时前
数据库原理实验报告:在ider里搭建mysql数据库
数据库·mysql·实验报告
2401_841495644 小时前
【数据库开发】个人信息管理的数据库创建以及查询方法(最简单)
数据库·sql·mysql·sqlite·数据库开发·个人数据库·管理个人信息
lang201509285 小时前
MySQL 8.0动态TLS配置全解析
mysql
Python算法实战6 小时前
腾讯送命题:手写多头注意力机制。。。
人工智能·算法·面试·大模型·强化学习
dot to one6 小时前
Centos 7 环境下mysql的安装及配置
linux·mysql·centos
讨厌吃蛋黄酥6 小时前
🔥 JavaScript异步之谜:单线程如何实现“同时”做多件事?99%的人都理解错了!
前端·javascript·面试
冲上云霄的Jayden6 小时前
修改 Docker 容器中 MySQL 8.0 默认编码为 utf8mb4_unicode_ci
utf-8·mysql·ci/cd·docker·utf8mb4
一只自律的鸡6 小时前
【MySQL】第四章 排序和分页
数据库·mysql