简短结论先给出:在 MySQL InnoDB 的“读已提交(RC)”隔离级别下,MVCC 不能避免幻读;在“可重复读(RR)”隔离级别下,MVCC 本身能避免「快照读」的幻读,但并不能完全避免「当前读」的幻读,InnoDB 是通过 MVCC + Next-Key Lock 一起解决幻读问题的。
下面分层说明。
幻读指的是:
同一个事务中,两次相同的范围查询,第二次查到了第一次没有看到的新插入行。
例如:
SELECT * FROM t WHERE age > 20;
-- 另一个事务插入 age=25 并提交
SELECT * FROM t WHERE age > 20;
第二次查询多了一行,就是幻读。
MVCC(多版本并发控制)核心机制:
事务ID 和 回滚指针SELECT 是快照读在 RR(可重复读) 下:
✅ 所以:
RR 下,MVCC 能避免快照读中的幻读
MVCC 只解决「读」的问题,不解决「写」的问题。
以下情况是当前读:
SELECT ... FOR UPDATE;
SELECT ... LOCK IN SHARE MODE;
UPDATE / DELETE / INSERT;
当前读会读取最新已提交数据,不走快照。
举例(RR + 当前读):
SELECT * FROM t WHERE age > 20 FOR UPDATE;
-- 另一事务插入 age=25 并提交
SELECT * FROM t WHERE age > 20 FOR UPDATE;
此时可能看到新行 → 幻读
❌ MVCC alone 不能避免这种情况
在 RR 下,InnoDB 使用:
例如:
SELECT * FROM t WHERE age > 20 FOR UPDATE;
会锁住:
其他事务无法插入 age > 20 的新行
→ 当前读也不会幻读
| 隔离级别 | MVCC | 幻读是否可能 |
|---|---|---|
| Read Uncommitted | ❌ | 可能 |
| Read Committed (RC) | ✅ | 可能(每次新快照) |
| Repeatable Read (RR) | ✅ | 快照读不会,当前读靠锁 |
| Serializable | ✅ | 不会(强制串行) |
MVCC 能避免“快照读”的幻读,但不能单独避免“当前读”的幻读;MySQL InnoDB 在 RR 下是靠 MVCC + Next-Key Lock 共同避免幻读的。
如果你愿意,我也可以用事务执行时序图或具体 SQL 示例来演示幻读是怎么发生的。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。