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

相关推荐
indexsunny3 小时前
互联网大厂Java求职面试实战:微服务与Spring生态全攻略
java·数据库·spring boot·安全·微服务·面试·消息队列
沪漂阿龙3 小时前
别再让数据库“吃”脏数据了!一文讲透MySQL约束,从入门到精通
数据库·mysql
skiy3 小时前
java与mysql连接 使用mysql-connector-java连接msql
java·开发语言·mysql
心软小念5 小时前
金三银四,全网最详细的软件测试面试题总结
软件测试·面试·职场和发展
杨云龙UP5 小时前
mysqldump逻辑备份文件恢复总结:全库恢复、单库恢复,一篇讲明白
linux·运维·服务器·数据库·mysql·adb
ybwycx5 小时前
mysql重置root密码(适用于5.7和8.0)
数据库·mysql·adb
Wilber的技术分享5 小时前
【LeetCode高频手撕题 2】面试中常见的手撕算法题(小红书)
笔记·算法·leetcode·面试
色空大师6 小时前
【网站搭建实操(一)环境部署】
java·linux·数据库·mysql·网站搭建
软件测试媛7 小时前
软件测试常见的面试题(46道)
功能测试·面试·职场和发展
liqianpin18 小时前
MySQL官网驱动下载(jar包驱动和ODBC驱动)【详细教程】
数据库·mysql