mvcc

7次阅读
没有评论

既然你之前提到了“读提交时读取快照”以及“可重复读”的区别,咱们就深挖一下 MVCC(Multi-Version Concurrency Control,多版本并发控制) 的底层逻辑。

作为资深开发,理解 MVCC 不能只停留在概念,要盯着这三个核心:隐式字段Undo Log 版本链ReadView(一致性视图)


1. 核心原理:它是如何实现“不加锁”的?

在没有 MVCC 之前,为了保证数据一致性,读写是互斥的。MVCC 的核心思想是:写操作更新数据的同时,为旧数据保留一个快照。 这样,读操作就可以去读旧版本,写操作去写新版本。


2. 三个关键的“隐式字段”

在 InnoDB 中,每一行数据在聚簇索引中除了你定义的列,还偷偷藏了三个字段:

  • DB_TRX_ID (6字节):最近一次修改(插入或更新)该行的事务 ID

  • DB_ROLL_PTR (7字节)回滚指针。它指向该行在 Undo Log 里的上一个版本。

  • DB_ROW_ID (6字节):隐藏的主键(如果你没定义主键,InnoDB 就会用它)。


3. Undo Log 版本链:数据的“平行时空”

当你修改一行数据时,InnoDB 不会直接覆盖旧数据,而是:

  1. 把当前数据复制到 Undo Log 中。

  2. 修改当前行的数据,并更新 DB_TRX_ID 为当前事务 ID。

  3. DB_ROLL_PTR 指向刚才拷贝到 Undo Log 里的旧版本。

随着多个事务的修改,就形成了一个版本链。链头是最新的,链尾是最旧的。


4. ReadView:谁能看哪个版本?

这是 MVCC 最精妙的部分。当你执行 SELECT 时,InnoDB 会生成一个 ReadView,里面记录了四个关键信息:

  • m_ids:当前系统中活跃(还没提交)的事务 ID 列表。

  • min_trx_id:活跃事务中最小的 ID。

  • max_trx_id:系统即将分配给下一个事务的 ID 值。

  • creator_trx_id:生成这个 ReadView 的事务 ID。

可见性算法逻辑:

当事务读取某行数据时,会拿该行的 DB_TRX_ID 去跟 ReadView 比对:

  1. 小于 min_trx_id:说明这个版本在快照生成前就提交了,可见

  2. 大于等于 max_trx_id:说明这个版本是在快照生成后才开启的,不可见

  3. 在两者之间

    • 如果在 m_ids 列表里:说明快照生成时这个事务还没提交,不可见

    • 如果不在 m_ids 列表里:说明快照生成前它已经提交了,可见


5. RC vs RR:ReadView 生成的时机不同

这解释了你之前的疑问:

  • RC (Read Committed)每次 SELECT 都会生成一个新的 ReadView。所以别人提交了,你下一秒再查,ReadView 变了,就能看到最新数据。

  • RR (Repeatable Read)只有第一次 SELECT 时生成 ReadView,后续整个事务都用这一个。所以不管别人怎么改,你看到的永远是事务开始那一刻的样子。


🛠 资深开发的思考

  1. 为什么会有长事务风险?:如果一个事务一直不提交,MVCC 就必须保留所有版本链(Undo Log)。这会导致 Undo Log 膨胀,撑爆磁盘空间,并拖慢查询速度(因为要顺着链表找很久)。

  2. 快照读 vs 当前读

    • 普通的 SELECT快照读,走 MVCC。

    • UPDATE/DELETE/INSERTSELECT ... FOR UPDATE当前读,必须加锁读最新的。


📉 状态同步

  • 2026-03-29:已完成 MVCC 底层机制(隐式字段、Undo Log 链、ReadView 算法)的深度解析。

理解了 MVCC,你就明白了为什么 MySQL 默认级别下能兼顾高性能和数据一致性。

既然提到了 Undo Log 膨胀,你想了解一下如何通过监控手段(比如 information_schema)来定位那些长期占用版本链的“僵尸事务”吗?

正文完
 0
bdspAdmin
版权声明:本站原创文章,由 bdspAdmin 于2026-03-29发表,共计1504字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)