温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

MVCC能避免幻读吗

发布时间:2026-09-30 09:49:20 来源:亿速云 阅读:93 作者:小樊 栏目:数据库

简短结论:在 MySQL InnoDB 的“可重复读(REPEATABLE READ)”隔离级别下,MVCC 能很大程度上避免“快照读”的幻读,但不能完全避免“当前读”的幻读;在标准 SQL 语义和某些场景下,MVCC 本身并不能完全避免幻读。

下面分层说明。


1. 什么是幻读

幻读(Phantom Read)指:

  • 同一个事务内
  • 两次执行相同的范围查询
  • 第二次查询看到了第一次查询时不存在的“新插入行”

注意:幻读强调的是insert 导致的新行,不是 update 导致的数值变化。


2. MVCC 是怎么工作的

MVCC(多版本并发控制)核心思想:

  • 每行数据有隐藏的版本信息(事务ID、回滚指针)
  • 读操作默认读快照(snapshot)
  • 事务只能看到:
    • 自己开启前已提交的数据
    • 自己事务内修改的数据

在 REPEATABLE READ 下:

  • 事务第一次读时生成一致性快照
  • 后续快照读都基于该快照
  • 其他事务新插入并提交的数据,对当前事务不可见

✅ 所以:
普通 SELECT(快照读)不会看到幻行


3. MVCC 能避免幻读的情况 ✅

示例(MySQL InnoDB,RR 级别):

-- 事务A
START TRANSACTION;
SELECT * FROM user WHERE age > 20;  -- 看到 2 条

-- 事务B
INSERT INTO user(age) VALUES (25);
COMMIT;

-- 事务A
SELECT * FROM user WHERE age > 20;  -- 仍然看到 2 条(MVCC 生效)

✅ 这里 MVCC 避免了幻读


4. MVCC 不能避免幻读的情况 ❌

场景一:当前读(Current Read)

以下语句不是快照读,而是当前读:

  • SELECT ... FOR UPDATE
  • SELECT ... LOCK IN SHARE MODE
  • UPDATE / DELETE

示例:

-- 事务A
START TRANSACTION;
SELECT * FROM user WHERE age > 20 FOR UPDATE;

-- 事务B
INSERT INTO user(age) VALUES (25);
COMMIT;

-- 事务A
SELECT * FROM user WHERE age > 20 FOR UPDATE;
-- 看到了新插入的行 → 幻读

❌ MVCC alone 不够

✅ 解决方式:Next-Key Lock(间隙锁 + 记录锁)


5. MySQL 为什么“看似”避免了幻读

InnoDB 在 REPEATABLE READ 下:

  • 快照读:MVCC
  • 当前读:Next-Key Lock

两者结合,使得 MySQL 的 RR 级别在工程中几乎避免了幻读

但这是:

MVCC + 锁机制 共同的结果
不是 MVCC 单独完成的


6. 标准 SQL 视角

  • SQL 标准定义:
    • REPEATABLE READ 允许幻读
    • SERIALIZABLE 避免幻读
  • MySQL 是“实现更强”的 RR

7. 总结一句话

MVCC 能避免快照读下的幻读,但不能避免当前读下的幻读;MySQL 通过 MVCC + Next-Key Lock 在 RR 级别几乎完全避免幻读。

如果你愿意,我也可以用图解版本链 / 快照生成时机 / 锁范围示例来进一步说明。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI
助
手