news 2026/7/8 3:53:23

MySQL InnoDB的 MVCC 实现机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL InnoDB的 MVCC 实现机制

MySQL InnoDB的 MVCC 实现机制

  • 1. MVCC概述
    • 什么是 MVCC
    • 当前读和快照读
    • MVCC 与锁机制的组合
  • 2. MVCC的实现原理
    • 隐式字段:记录的版本元数据
    • Undo Log:版本链的存储载体
      • Undo Log 的分类
      • 版本链的形成过程
    • Read View:版本可见性的判断规则
    • 不同隔离级别下的 Read View

1. MVCC概述

什么是 MVCC

MVCC(Multi-Version Concurrency Control),即多版本并发控制,是数据库领域中一种核心的并发控制技术,不仅广泛应用于数据库管理系统(DBMS)以支撑高并发的数据库访问场景,也在部分编程语言的事务内存实现中发挥重要作用。

在 MySQL 的 InnoDB 存储引擎中,MVCC 的核心设计目标是提升数据库的并发处理能力。传统的锁机制在处理读写冲突时,往往需要通过加锁阻塞来保证数据一致性,这会显著降低并发效率;而 MVCC 创新性地解决了读 - 写冲突问题 —— 即便存在读写操作的并发执行,也能做到不加锁、非阻塞的并发读,极大地优化了数据库在高并发场景下的性能表现。

当前读和快照读

要理解 MVCC 的工作逻辑,首先需要区分数据库中的两种核心读取方式:当前读和快照读。

1、当前读

当前读是指读取记录的最新版本,且为了保证数据的一致性和排他性,读取过程中会对目标记录加锁,防止其他并发事务修改该记录。典型的当前读操作包括:

  • 加共享锁的查询:SELECT ... LOCK IN SHARE MODE,其他事务可读取该记录,但无法修改,直到共享锁释放;
  • 加排他锁的操作:SELECT ... FOR UPDATE、UPDATE、INSERT、DELETE,这类操作会获取记录的排他锁,其他事务既无法读取(加锁读)也无法修改该记录,直到排他锁释放。

当前读的本质是悲观锁的实现,通过强制加锁的方式规避并发修改风险,但也不可避免地带来了锁竞争和阻塞问题

2、快照读

快照读是指不加锁的非阻塞读取,也是日常开发中最常用的读取方式(如普通的SELECT操作)。快照读的核心支撑就是 MVCC—— 它并不直接读取记录的最新版本,而是读取数据的某个历史版本(快照),从而避免了与写操作的锁冲突

可以将 MVCC 理解为行锁的一种 “变种”,但它在绝大多数场景下避免了显式加锁操作,大幅降低了锁机制带来的性能开销。需要明确的是:MVCC 解决的是快照读与写操作之间的冲突,而非当前读的冲突

MVCC 与锁机制的组合

数据库的并发冲突主要分为两类:读 - 写冲突、写 - 写冲突。MVCC 本身无法解决写 - 写冲突,因此需要与锁机制结合,形成两种经典的组合方案:

  • MVCC + 悲观锁:MVCC 处理读 - 写冲突(快照读无锁),悲观锁(如行锁、表锁)处理写 - 写冲突(通过加锁阻塞避免并发修改);
  • MVCC + 乐观锁:MVCC 处理读 - 写冲突,乐观锁(如基于版本号 / 时间戳的校验)处理写 - 写冲突(无锁阻塞,通过版本比对解决冲突)。

这种组合模式既保证了数据一致性,又最大化了数据库的并发处理能力,也是 InnoDB 成为 MySQL 默认存储引擎的核心原因之一


2. MVCC的实现原理

MVCC 的实现并非单一技术,而是由记录的隐式字段Undo Log(回滚日志)Read View(读视图)三大核心组件协同完成

隐式字段:记录的版本元数据

InnoDB 中,每行记录除了用户自定义的字段(如name、age)外,还会自动生成若干隐式字段,用于维护记录的版本信息,核心隐式字段包括:

  1. DB_ROW_ID6byte,隐含的自增主键(隐藏主键)。若数据表未定义主键,InnoDB 会以此生成聚簇索引
  2. DB_TRX_ID6byte,事务 ID:记录创建 / 最后一次修改该记录的事务 ID(事务 ID 自增分配)
  3. IDDB_ROLL_PTR 7byte,回滚指针:指向该记录上一个版本的 Undo Log(形成版本链表)
  4. DELETED_BIT1byte,删除标记:标记记录是否被 “删除”(逻辑删除,而非物理删除)

核心说明:

  • DB_ROW_ID仅在表无主键时生效,是 InnoDB 保证聚簇索引存在的兜底方案;
  • DB_TRX_ID是版本判断的核心标识,每一次修改记录都会更新该字段为当前事务 ID;
  • DB_ROLL_PTR是连接历史版本的关键,通过它可以追溯记录的所有历史版本;
  • DELETED_BIT是 InnoDB逻辑删除的实现 —— 删除操作仅修改该标记,不会立即物理删除记录,为 MVCC 的版本链提供支持。

Undo Log:版本链的存储载体

Undo Log(回滚日志)是 InnoDB 为事务回滚和 MVCC 提供的核心日志,存储在磁盘的rollback segment 中。与 Redo Log(重做日志)保障事务持久性不同,Undo Log 的核心作用是:记录数据修改前的旧版本,支持事务回滚,同时为 MVCC 提供历史版本数据

Undo Log 的分类

由于查询操作(SELECT)并不会修改任何用户记录,所以在查询操作执行时,并不需要记录相应的undo log

根据操作类型,Undo Log 分为三类,其中对 MVCC 最关键的是Update undo log

  • Insert Undo Log,仅在插入操作时生成,记录插入记录的主键值。事务回滚时,只需根据主键删除该记录即可,对 MVCC 无直接作用
  • Update Undo Log,修改记录时生成,记录修改前的完整旧版本数据。这是 MVCC 的核心依赖 —— 每一次更新操作都会将旧版本数据写入Update Undo Log,并通过DB_ROLL_PTR将当前记录与旧版本链接,形成版本链表
  • Delete Undo Log,删除操作时生成,记录被删除记录的完整内容。InnoDB 的删除操作是 逻辑删除 —— 仅将DELETED_BIT标记为 1,而非物理删除记录;事务回滚时,只需将DELETED_BIT改回 0,并恢复记录即可

版本链的形成过程

以person表的一条记录为例,

1、步骤 1:初始插入记录

事务 T0 插入一条记录:name=Jerry,age=24,此时隐式字段状态:

  • DB_ROW_ID=1(自动生成);
  • DB_TRX_ID=NULL(无修改事务);
  • DB_ROLL_PTR=NULL(无历史版本);
  • DELETED_BIT=0(未删除)。

2、步骤 2:事务 T1 修改记录(name→Tom)

事务 T1 执行UPDATE person SET name='Tom' WHERE DB_ROW_ID=1,执行流程:

  • 对该记录加排他锁,防止并发修改;
  • 将当前记录的完整数据(name=Jerry,age=24)拷贝到 Update Undo Log,作为第一个历史版本;
  • 修改记录的name为 Tom,更新DB_TRX_ID为 T1 的事务 ID(假设为 1);
  • DB_ROLL_PTR指向步骤 2 中生成的 Undo Log 记录;
  • 事务提交,释放排他锁。

此时,记录的最新版本指向 T1 的修改,Undo Log 中保留了初始版本,版本链初步形成

3、步骤 3:事务 T2 修改记录(age→30)

事务 T2 执行UPDATE person SET age=30 WHERE DB_ROW_ID=1,执行流程:

  • 对该记录加排他锁;
  • 将当前记录的完整数据(name=Tom,age=24)拷贝到 Update Undo Log,作为新的历史版本(插入版本链的头部);
  • 修改记录的age为 30,更新DB_TRX_ID为 T2 的事务 ID(假设为 2);
  • DB_ROLL_PTR指向步骤 2 中生成的新 Undo Log 记录;
  • 事务提交,释放排他锁。

最终,该记录的版本链结构为:最新版本(name=Tom,age=30,TRX_ID=2) → 历史版本1(name=Tom,age=24,TRX_ID=1) → 历史版本2(name=Jerry,age=24,TRX_ID=NULL)

版本链的核心特征:链首是最新的历史版本,链尾是最早的初始版本,通过DB_ROLL_PTR串联

Read View:版本可见性的判断规则

当事务执行快照读时,InnoDB 会生成一个 Read View(读视图),它是当前事务可见的事务 ID 集合的快照,核心作用是:判断当前事务能看到版本链中的哪个版本

Read View 包含四个关键属性(以 RC 隔离级别为例):

  • m_ids:生成 Read View 时,系统中所有活跃的事务 ID 列表(未提交的事务);
  • min_trx_id:m_ids中的最小值(最早开启的活跃事务);
  • max_trx_id:生成 Read View 时,系统即将分配的下一个事务 ID;
  • creator_trx_id:生成该 Read View 的当前事务 ID。

Read View 通过对比记录的DB_TRX_ID(事务 ID)与自身属性,判断该版本是否对当前事务可见,判断规则如下:

  • 若记录的DB_TRX_ID == creator_trx_id:该记录由当前事务修改,可见;
  • 若记录的DB_TRX_ID < min_trx_id:修改该记录的事务已提交,可见;
  • 若记录的DB_TRX_ID >= max_trx_id:修改该记录的事务在生成 Read View 后才开启,不可见;
  • min_trx_id ≤ DB_TRX_ID < max_trx_id
    DB_TRX_ID在m_ids中(事务未提交),不可见;
    DB_TRX_ID不在m_ids中(事务已提交),可见。

若当前版本不可见,则通过DB_ROLL_PTR遍历版本链,依次判断下一个历史版本,直到找到第一个可见的版本,这就是当前事务快照读能看到的最终数据。

不同隔离级别下的 Read View

MySQL 的隔离级别(读未提交、读已提交、可重复读、串行化)中,Read View 的生成策略是区分 “读已提交(RC)” 和 “可重复读(RR)” 的核心:

  • 读已提交(RC),每次执行快照读时,生成新的 Read View,这样,同一事务中多次快照读,可能看到不同版本(不可重复读)
  • 可重复读(RR),事务中第一次执行快照读时,生成 Read View,后续复用,这样,同一事务中多次快照读,看到的是同一版本(可重复读)

InnoDB 的可重复读,本质是通过 “复用 Read View” 实现的 —— 事务第一次快照读生成的 Read View,会被该事务后续所有快照读复用,因此即便其他事务提交了修改,当前事务也不会看到,保证了 “可重复读” 的特性。

例如:事务 A(RR 隔离级别)第一次快照读生成 Read View(m_ids=[2,3]),此时事务 B(TRX_ID=4)修改了记录并提交;事务 A 后续的快照读仍复用最初的 Read View,因此看不到事务 B 的修改,实现了可重复读。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/29 22:48:58

中小品牌必备!2026年高性价比软文推广平台TOP5

对于预算有限的中小品牌&#xff0c;软文推广是实现品牌曝光与产品营销的性价比之选。然而&#xff0c;市场上发稿平台众多、质量参差&#xff0c;如何选择可靠、高效且能最大化预算价值的合作伙伴&#xff0c;成为市场决策者的关键挑战。真正的“高性价比”应是资源精准度、执…

作者头像 李华
网站建设 2026/7/1 8:12:46

模运算

lc3345 直接 n) % n 会越界&#xff0c;要 % n n) % n]; // 保证结果在 [0,n-1] 中 class Solution { public: vector<int> constructTransformedArray(vector<int>& nums) { int n nums.size(); vector<int> result(n); …

作者头像 李华
网站建设 2026/7/1 22:07:03

基于Python+Django青岛滨海学院县志捐赠与借阅信息管理系统(源码+lw+部署文档+讲解等)

课题介绍 本课题针对青岛滨海学院县志捐赠与借阅管理中存在的县志档案杂乱、捐赠信息登记繁琐、借阅流程低效、归还提醒不及时、库存统计不便、捐赠者信息管理分散等痛点&#xff0c;设计并实现基于PythonDjango的青岛滨海学院县志捐赠与借阅信息管理系统。后端采用Python语言结…

作者头像 李华
网站建设 2026/7/1 3:36:27

软件测试公众号热度内容解析:专业视角下的三大爆款赛道

2026年&#xff0c;软件测试公众号的热度内容高度专业化&#xff0c;阅读量破万的文章集中于三大类型&#xff0c;均以解决从业者实际痛点为内核。这些内容如同鹤岗的“双极人生”&#xff0c;在技术深度&#xff08;代码&#xff09;与实用韧性&#xff08;冰雕&#xff09;间…

作者头像 李华
网站建设 2026/7/1 8:12:08

从职业烧伤到AI心理教练:开发者的自愈之路

在软件测试领域&#xff0c;职业倦怠&#xff08;俗称“职业烧伤”&#xff09;已成为普遍挑战&#xff0c;源于高强度工作、项目压力和创新需求匮乏。数据显示&#xff0c;2026年初&#xff0c;测试从业者离职率上升&#xff0c;部分原因包括长时间调试代码、应对紧急交付和缺…

作者头像 李华