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,串行化)或手动加表锁来彻底避免

相关推荐
掘金安东尼2 小时前
字节前端三面复盘:基础不花哨,代码要扎实(含高频题解)
前端·面试·github
顾林海2 小时前
从"面条代码"到"精装别墅":Android MVPS架构的逆袭之路
android·面试·架构
FogLetter2 小时前
面试官问我Function Call,我这样回答拿到了Offer!
前端·面试·openai
AAA修煤气灶刘哥3 小时前
日志排查不用慌!从采集到 ELK 实战,手把手教你搞定线上问题
后端·面试·debug
就是帅我不改3 小时前
SpringBoot多租户架构设计终极指南:5种方案彻底解决企业级SaaS隔离难题
后端·面试·架构
秋难降3 小时前
零基础学SQL(八)——事务
数据库·sql·mysql
Starry_hello world3 小时前
MySql 表的约束
数据库·笔记·mysql·有问必答
页面仔Dony3 小时前
任务队列的中断和恢复实现
javascript·面试
言兴3 小时前
深度解析:React Fiber 机制 —— 从“同步阻塞”到“可中断调度”的革命
前端·javascript·面试