程序员面试宝典

一站式面试准备平台

返回分类
mysql高级

Mysql mvcc 原理深度解析

深入理解 mvcc 事务

2026-03-28
阅读时间: 6分钟

一 MVCC 的核心目标:读写不阻塞

在传统的锁机制中,写操作会阻塞读操作。而 MVCC 让 InnoDB 实现了:

  • 非锁定读(Consistent Read):普通的 SELECT 不加锁。
  • 并发读写:写操作时,读操作可以读取数据的“历史版本”,互不干扰。

MVCC,即多版本并发控制。在MySQL InnoDB中,它的核心目的是解决读写冲突,实现非锁定一致性读。简单来说,它让SELECT操作在读取数据时,不需要等待UPDATEDELETE释放锁,而是通过读取数据行的历史快照来返回结果,从而极大提升数据库的并发性能。


二 核心三要素

1. 隐藏字段

每行数据除了我们定义的列,还有三个隐藏字段:

  • DB_TRX_ID:最后修改(插入/更新/删除)该行的事务ID。
  • DB_ROLL_PTR:回滚指针,指向该行数据在undo log中的历史版本。
  • DB_ROW_ID:隐含的自增ID(如果表没有主键时才会用到,面试时可简略带过)。

2. undo log(版本链)

  • 当一行数据被修改时,InnoDB会先将旧数据写入undo log。
  • 通过 DB_ROLL_PTR,最新数据指向旧数据,形成一条版本链
  • 这条链就是MVCC能够“回溯”到历史版本的依据。

3. Read View(读视图)

这是核心控制逻辑。当执行SELECT(快照读)时,会生成一个Read View,它包含几个关键属性:

  • m_ids:生成Read View时,当前系统正在执行、还没提交的事务 ID 列表。
  • min_trx_id:m_ids中的最小值。
  • max_trx_id:生成Read View时系统应该分配给下一个事务的ID(即m_ids中最大值+1)。
  • creator_trx_id:当前执行SELECT操作的事务自己的ID。

三 给定一个版本链,怎么判断哪个版本对当前事务可见?

规则如下: 当事务尝试读取某行记录时,会拿着该记录的 DB_TRX_ID 与 Read View 进行对比,逻辑如下:

  1. 如果 DB_TRX_ID == creator_trx_id → 当前事务自己改的,可见。
  2. 如果 DB_TRX_ID < min_trx_id → 该版本在生成Read View前已经提交了,可见。
  3. 如果 DB_TRX_ID >= max_trx_id → 该版本是生成Read View之后才开启的事务,不可见。
  4. 如果 min_trx_id <= DB_TRX_ID < max_trx_id → 检查 DB_TRX_ID 是否在 m_ids 列表中。
    • 如果在(即还未提交):不可见,顺着 DB_ROLL_PTR 找上一个版本。
    • 如果不在(即已经提交了):可见。

如果当前版本不可见,就顺着回滚指针去 Undo Log 找上一个版本,重复上述判断。


四 RC 与 RR 隔离级别的区别

MVCC 在不同的隔离级别下,生成 Read View 的时机不同,这直接导致了现象的差异:

  • READ COMMITTED (RC)每次 SELECT 都会生成一个新的 Read View。所以能看到其他事务已提交的修改(导致不可重复读)。
  • REPEATABLE READ (RR)只在第一次 SELECT 时生成 Read View,后续所有查询复用同一个。所以能保证在整个事务期间看到的数据是一致的。

五 延展

快照读和当前读的区别?

  • 快照读:普通的SELECT语句(不加锁),读取的是Read View下的可见版本,不等待锁。
  • 当前读SELECT ... FOR UPDATEUPDATEDELETEINSERT等操作,读取的是数据的最新版本,并且会对记录加锁(行锁/间隙锁)。

一句话总结:MVCC让快照读无锁,当前读负责数据一致性。

RR级别下能完全避免幻读吗?

  • MVCC通过Read View在RR级别下避免了快照读的幻读(即同一事务内多次SELECT结果一致)。
  • 但如果是当前读(比如SELECT ... FOR UPDATE),MySQL会结合**Next-Key Lock(间隙锁+行锁)**来防止其他事务插入新数据,从而彻底解决幻读。

长事务会有什么问题?

  • 因为MVCC依赖undo log构建历史版本,如果存在一个长事务,它的Read View会一直保留,导致undo log不能被清理。
  • 后果:undo log膨胀,占用大量存储空间,甚至导致性能下降(“undo log爆炸”)。

六 总结: MVCC 解决了什么?

“总的来说,MVCC通过隐藏字段 + undo log版本链 + Read View可见性判断,实现了读写并发的高效处理。它是InnoDB区别于MyISAM、实现高并发OLTP场景的核心能力之一。在实际开发中,理解MVCC能帮助我们更好地设计事务粒度,避免长事务,并准确判断不同隔离级别下的数据可见性。”

  • 解决了脏读:通过版本链和 Read View 过滤未提交数据。
  • 解决了不可重复读:RR 级别下复用 Read View。
  • 很大程度上缓解了幻读:通过快照读避开了其他事务插入的新记录(虽然彻底解决幻读还需配合 Next-Key Locks)。

注意:MVCC 只在 RCRR 两个隔离级别下工作。Serializable(串行化)通过加锁实现,Read Uncommitted(读未提交)直接读最新数据,都不需要 MVCC。

相关标签