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

相关推荐
测试19988 小时前
2026最新软件测试面试八股文【附文档】
自动化测试·软件测试·python·测试工具·面试·职场和发展·测试用例
zmsofts9 小时前
java面试必问13:MyBatis 一级缓存、二级缓存:从原理到脏数据,一篇讲透
java·面试·mybatis
我叫黑大帅9 小时前
为什么map查找时间复杂度是O(1)?
后端·算法·面试
Bert.Cai9 小时前
MySQL DML简介
数据库·mysql
M ? A10 小时前
Vue 动态组件在 React 中,VuReact 会如何实现?
前端·javascript·vue.js·经验分享·react.js·面试·vureact
考虑考虑11 小时前
SQL语句中的order by可能造成时间重复
数据库·后端·mysql
SPC的存折13 小时前
D在 Alpine 容器中手动搭建 Discuz 全攻略(包含镜像一键部署脚本,可直接用)
linux·数据库·mysql·缓存
AgCl2313 小时前
MYSQL-6-函数与约束-3/17
android·数据库·mysql
junqiduhang13 小时前
Win11 MySQL 8.0 安装八步走
数据库·mysql
zzb158014 小时前
Fragment 生命周期深度图解:从 onAttach 到 onDetach 完整流程(面试必备)
android·java·面试·安卓