小工具知识网

当前位置: 首页 > 行业知识 > MVCC【重点】

行业知识

MVCC【重点】

2026-10-19 05:28:17 来源:互联网转载

参考��接

[1] https://www.bilibili.com/video/BV1YD4y1J7Qq/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=0cb0c5881f5c7d76e7580fbd2f551074

[2]https://www.cnblogs.com/jelly12345/p/14889331.html

[3]https://xiaolincoding.com/mysql/transaction/mvcc.html#read-view-%E5%9C%A8-mvcc-%E9%87%8C%E5%A6%82%E4%BD%95%E5%B7%A5%E4%BD%9C%E7%9A%84

[4]https://zhuanlan.zhihu.com/p/52977862

MVCC(Multi-Version Concurrency Control)多版本并发控制,MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读

数据库并发场景有三种,分别为:

  • 读-读:不存在任何问题,也不需要并发控制
  • 读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
  • 写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失

    核心知识点

    MVCC它的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View 来实现的。

    1、3个隐式字段
    2、聚簇索引
    3、undo log
    4、 read view

    1、3个隐式字段

    每行记录除了我们自定义的字段外,还有数据库隐式定义的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段

    • DB_ROW_ID:6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引

    • DB_ROLL_PTR:7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)

      • DB_TRX_ID:6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
      • 实际还有一个删除flag隐藏字段, 既记录被更新或删除并不代表真的删除,而是删除flag变了

        DB_ROW_ID是数据库默认为该行记录生成的唯一隐式主键;DB_ROLL_PTR是一个回滚指针,用于配合undo日志,指向上一个旧版本;DB_TRX_ID是当前操作该记录的事务ID。

        2. 聚簇索引

        在MySQL中,InnoDB存储引擎默认使用聚簇索引来组织表的数据。

        • 如果表定义了主键,则主键索引就是聚簇索引;【不隐藏】
        • 如果表没有定义主键,则InnoDB存储引擎会选择一个唯一的非空索引来作为聚簇索引;【不隐藏】
        • 如果没有合适的唯一非空索引,则InnoDB会自动生成一个隐藏的聚簇索引。

          下面这张图,主键为id,所以聚簇索引为id,这时候并不是一个隐藏的列

          例如下面第一张图由于没有主键,没有唯一非空索引,因此使用自动生成当做聚簇索引

          =====================================================================================

          MVCC如何实现事务隔离?(Undo log版本链和Readview)

          MySQL中MVCC的实现是基于Undo log版本链和Readview来达成的。

          =====================================================================================

          3. undo log(回滚日志)-------构造版本链

          undo log(回滚日志),它保证了事务的 ACID 特性 (opens new window)中的原子性(Atomicity)

          undo log主要分为两种:

          • insert undo log:代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃
          • update undo log: 事务在 进行update或delete 时产生的undo log;不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除

            对MVCC有帮助的实质是update undo log ,undo log实际上就是存在rollback segment中旧记录链,

            Undo log(回滚日志),作为回滚的基础,在执行update和delete操作时,会将每次操作详细记录在Undo log中,每条Undo log中都记录一个叫做roll_pointer的引用信息,通过roll_pointer就可以将某条数据对应的Undo log,链接成一个Undo链。在Undo链的头部,通过数据行中的roll_pointer进行关联,就可以构成一条数据的版本链,这样对于每一行记录,都会有一个版本链,体现了这条记录的所有变更,有事务对这条数据进行操作时,将操作后的数据链接到版本的头部。

            补充:

            因此,undo log 两大作用:

            1. 实现事务回滚,保障事务的原子性。 事务处理过程中,如果出现了错误或者用户执 行了 ROLLBACK 语句,MySQL 可以利用 undo log 中的历史数据将数据恢复到事务开始之前的状态。
            2. 实现 MVCC(多版本并发控制)关键因素之一。 MVCC 是通过 ReadView + undo log 实现的。undo log 为每条记录保存多份历史数据,MySQL 在执行快照读(普通 select 语句)的时候,会根据事务的 Read View 里的信息,顺着 undo log 的版本链找到满足其可见性的记录。

            4. 每条数据的版本链都构造好之后,在查询的时候,是如何选择版本?(Read View)

            就需要使用到Read View结构来实现。

            Read View(读视图),是一个视图一个内存结构,一个 Read View 会在事务开始时创建(针对不同情况而定)。

            对于 「读提交」和「可重复读」 隔离级别的事务来说,它们的 快照读【补充部分:当前读,快照读和MVCC的关系】(普通 select 语句)是通过 Read View + undo log 来实现的,它们的区别在于 创建 Read View 的时机不同 :

            1. 「读提交」隔离级别是在每个 select 都会生成一个新的 Read View,也意味着,事务期间的多次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事务修改了该记录,并提交了事务。
            2. 「可重复读」隔离级别是启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View,这样就保证了在事务期间读到的数据都是事务启动前的记录。

            这两个隔离级别实现是通过「事务的 Read View 里的字段」和「记录中的两个隐藏列(trx_id 和 roll_pointer)」的比对,如果不满足可见行,就会顺着 undo log 版本链里找到满足其可见性的记录,从而控制并发事务访问同一个记录时的行为,这就叫 MVCC(多版本并发控制)。

            因此我们可以知道在 「读提交」和「可重复读」的实现都是通过MVCC模式完成的

            补充:

            当前读,快照读和MVCC的关系

            • 准确的说,MVCC多版本并发控制指的是 “维持一个数据的多个版本,使得读写操作没有冲突” 这么一个概念。仅仅是一个理想概念
            • 而在MySQL中,实现这么一个MVCC理想概念,我们就需要MySQL提供具体的功能去实现它,而快照读就是MySQL为我们实现MVCC理想模型的其中一个具体非阻塞读功能。【MVCC是一种通用模式,快照读是MySQL实现MVCC的其中一个具体非阻塞读功能】 而相对而言,当前读就是悲观锁的具体功能实现
            • 要说的再细致一些,快照读本身也是一个抽象概念,再深入研究。MVCC模型在MySQL中的具体实现则是由 3个隐式字段,undo日志 ,Read View 等去完成的,具体可以看下面的MVCC实现原理。

              当一个事务开始时,数据库系统会根据当前的活跃事务状态创建一个 Read View,用于确定该事务在其执行期间可以看到的数据视图。这个数据视图包含4个信息:

              1. creator_trx_id:创建Read View的事务;
              2. m_ids:当前活跃且未提交的事务集合 **(可以存在多个id) **
              3. min_trx_id:前活跃且未提交集合m_ids中最小事务的id
              4. max_trx_id:下一个将被分配的事务的id值,该值为全局事务中最大的事务 id 值 + 1 ==id+1;。

              事务在执行期间会使用这个 Read View 来确定哪些数据行是可见的,哪些是不可见的。(例如下面的例子,【T5】可以/不可以看到【T1、T2、T3、T4】中的哪些)因此,创建一个 Read View 是为了在事务执行期间保持数据的一致性和隔离性。访问规则:

              1. 一个事务去访问记录的时候,自己的更新记录总是可见
              2. 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,【 trx_id
              3. 如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值 【 trx_id >= max_trx_id】,表示这个版本的记录是在创建 Read View 后才启动的事务生成的,所以该版本的记录对当前事务 不可见。
              4. 如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间 【min_trx_id

上一篇:哪里的双线机房好?

下一篇:无锡市城镇职工录用登记备案表