真实面经题目 · 原创解析
MVCC 的实现原理是什么?
MVCC 是多版本并发控制,通过为数据保留多个历史版本,让读操作基于一致性快照判断可见版本,从而减少读写互相阻塞。常见实现依赖隐藏版本字段、undo log、ReadView 和可见性规则。
真实面经题目 · 原创解析
MVCC 是多版本并发控制,通过为数据保留多个历史版本,让读操作基于一致性快照判断可见版本,从而减少读写互相阻塞。常见实现依赖隐藏版本字段、undo log、ReadView 和可见性规则。
MVCC 的核心是同一行数据可以存在多个版本,读事务根据自己的快照判断哪个版本可见,写事务生成新版本而不是直接阻塞所有普通读。以 InnoDB 为例,行记录中有事务 id 和回滚指针,更新时旧版本进入 undo log,新版本记录当前事务 id。快照读会创建 ReadView,里面包含当前活跃事务集合、最小活跃事务 id、下一个待分配事务 id 等信息,然后沿版本链判断记录是否对当前事务可见。读已提交通常每条语句生成新的 ReadView,可重复读通常事务内首次快照读生成并复用 ReadView。当前读不同于快照读,会读取最新版本并加锁。
传统锁并发控制中,读写之间容易互相阻塞。MVCC 的目标是让普通读操作读取某个时间点的一致性版本,同时允许写操作继续产生新版本,从而提升读写并发能力。它不是完全不要锁,而是让一致性快照读尽量不依赖锁;更新、删除和加锁查询等当前读仍然需要锁保证正确性。
一行数据被更新时,数据库不会简单覆盖后丢弃旧值,而是通过 undo log 保存旧版本,并在记录中维护指向旧版本的回滚指针。新版本带有创建它的事务标识,旧版本沿指针串成版本链。查询时如果当前版本不可见,就沿版本链向历史版本查找,直到找到可见版本或确认记录不可见。
ReadView 可以理解为事务创建快照时的并发状态描述,通常包含活跃事务 id 集合、最小活跃事务 id、下一个待分配事务 id 等信息。判断记录版本是否可见时,会比较版本的事务 id 和这些边界:快照之前已提交的版本可见,快照之后产生的版本不可见,快照时仍活跃事务产生的版本也不可见。
MVCC 和隔离级别密切相关。在读已提交下,每条一致性读语句通常都会生成新的 ReadView,因此同一事务内两次查询可能看到其他事务刚提交的数据。在可重复读下,事务内首次一致性读生成的 ReadView 通常会被复用,所以后续快照读看到的是同一份逻辑快照。
MVCC 需要保留历史版本,但历史版本不能无限增长。数据库会在确认没有事务需要某些旧版本后,通过 purge 等机制清理 undo 记录。长事务会持有较早的 ReadView,阻止旧版本回收,导致 undo 膨胀、版本链变长和存储压力上升,因此线上系统要警惕长事务。
快照读读取符合 ReadView 的历史一致版本,常见普通 select 属于这一类。当前读读取最新版本并通常加锁,例如 update、delete、select for update 和 lock in share mode。
在快照读场景下,可重复读通过固定 ReadView 可以避免同一事务内看到新的幻影记录。但当前读涉及最新数据和范围修改,仍需要 next-key lock 等锁机制配合。
长事务持有较早的 ReadView,数据库必须保留它可能读取到的旧版本,导致 undo log 无法及时清理。版本链变长后,查询历史版本的成本也可能增加。
关键是 ReadView 的生成时机。读已提交通常每条语句创建新 ReadView,所以能看到其他事务新提交的数据;可重复读通常事务内复用首次快照读的 ReadView,所以结果更稳定。