MySQL 的 MVCC 是什么?Read View 如何决定一条记录是否可见?
🧑💻 面试官:另一个事务修改了余额,为什么我的普通 SELECT 还读到旧值?
🙋♂️ 我:InnoDB 可以通过 MVCC 读取符合当前快照的版本。
🧑💻 面试官:版本多了以后,怎么知道应该看哪一个?是不是事务编号比我小,就都能读?
🙋♂️ 我:还要看创建读取视图时,那个事务是否已经提交。
🧑💻 面试官:那事务 BEGIN 以后才提交的记录,在 RR 下永远看不到吗?快照到底什么时候生成?
「有历史版本」只是第一步;Read View 规定当前这次读取能看见哪些事务产生的版本。
面试速答(60 秒版)
MVCC 是多版本并发控制。InnoDB 为记录保留版本信息,并通过 undo 记录回溯旧版本,让普通一致性读能够读取符合自己快照的数据,而不必总等待其他事务的写锁。
Read View 是判断版本是否可见的读取视图。它会记录创建时仍活跃的事务以及事务编号边界。自己的修改可以看见;视图创建前已经提交的修改通常可见;当时尚未提交或后来才产生的修改,对这份视图不可见。
在 READ COMMITTED 下,每次一致性读使用新的快照;在 REPEATABLE READ 下,通常第一次一致性读建立快照,后面继续使用,并不是普通 BEGIN 就一定建立。
同时,MVCC 主要解释一致性读。锁定读和写操作有自己的锁与当前读取规则,不能把所有 SQL 都当成读取旧快照。

知识点详解:同一行记录,怎样找到自己能看的版本?
旧版本存在哪里?
假设账户余额原来是 100。事务 B 把它改成 80,记录会带上相应事务信息,旧值可以通过 undo 记录回溯。
可以把它理解为一条版本链:当前版本不适合读取,就沿着 undo 找更早的版本,直到找到可见的记录,或者确定这行对当前视图不存在。
undo 不只是一个与读取无关的回滚按钮。它也支撑一致性读取旧版本。历史不是无限保留,已经不再需要的版本会由清理机制处理。InnoDB 多版本说明介绍了这些记录的用途。
Read View 判断的不是“值新不新”
为了理解规则,假设视图创建时,事务 10、12 仍活跃,下一批尚未分配的编号从 15 开始。当前读者的身份另行记录。
对某个记录版本,先判断是不是自己产生的修改:自己的修改可以看见。
如果版本来自 10 之前的事务,可以认为它在视图建立前已经结束,因此可见。如果版本编号达到 15 或更大,说明它属于视图建立以后分配的事务,对当前快照不可见。
中间的编号则要检查活跃列表。比如 12 当时仍在执行,不可见;11 已经不在活跃列表,则可以看见。这里讨论的是常见用户记录的简化规则,不展开系统事务等内部细节。
因此,“编号比自己小就可见”不对:较早开始的事务,也可能在创建视图时尚未提交。真正使用的是视图边界与活跃事务,而不只是两个编号大小。MySQL 8.4 的 Read View 实现可以核对这一判断。

B 后来提交,旧视图会不会自动改变?
不会因为 B 提交,就把已有视图的活跃列表重新改写。
假设 A 已经建立了一份视图,而 B 当时还没提交。B 后来提交余额 80,A 使用原来的视图做一致性读,仍可能沿版本链读到 100。
但 A 如果自己修改了记录,能看见自己的修改。这也意味着“事务里读到的所有内容必然属于某个单一历史时刻”是过度简化,不能忽略自己的写入。
RC 与 RR,差在何时取得视图
在 READ COMMITTED 下,后续一致性读会取得新的快照。A 第二次读取时,B 已经提交,就有机会看到 80。
在 REPEATABLE READ 下,通常沿用第一次一致性读建立的快照。A 第二次仍可能看到 100。
这里的“第一次一致性读”很重要。普通 BEGIN 后还没有读,B 已经提交,再执行第一次普通 SELECT,就可能把 B 的修改包含进去。显式使用 WITH CONSISTENT SNAPSHOT 等方式时,也要结合隔离级别和实际语句理解。
这些行为针对 InnoDB 的一致性读,官方一致性读说明给出了 RC 与 RR 的区别。SELECT FOR UPDATE 不能直接套用同一套快照结论。

面试官继续追问
MVCC 会让数据库完全不用锁吗?
不会。普通一致性读减少了读写之间的相互等待,但写操作、锁定读和约束检查仍然涉及锁。
两个事务同时改同一行,不能靠各自看一份旧版本就忽略写冲突。MVCC 与锁解决的事情不同。
长事务为什么影响 undo 清理?
如果某个活跃视图仍可能需要旧版本,系统就不能随便把相应历史全部清掉。
因此,长时间不结束的事务可能增加历史保留和清理压力。排查时应检查事务持续时间与相关历史指标,而不是只看当前执行的 SELECT 很快。
怎样验证自己有没有理解 RC 和 RR?
用两个独立连接,明确记录开始、第一次读、另一个事务提交、第二次读的顺序,分别测试两种隔离级别。
再改变第一次读的时机,并增加自己的写入与锁定读。必须控制时序,不能把一次偶然执行结果当成隔离规则。
面试速记卡
- MVCC:版本信息与 undo 支撑读取符合快照的旧版本。
- Read View:活跃事务和编号边界决定版本可见性。
- 自己的修改:可以看见,不能忽略这一例外。
- RC 与 RR:每次一致性读新快照,或通常沿用首次一致性读快照。
- 边界:快照读不等于锁定读,MVCC 不会消除所有锁。