MySQL InnoDB MVCC多版本并发控制原理详解

Nora
Nora
发布时间:2026-10-16| 4人浏览过
原创

MySQL InnoDB存储引擎通过多版本并发控制(MVCC)实现了高效的非阻塞读写操作。本文详细拆解MVCC的核心概念、当前读与快照读的区别,以及基于隐藏字段、Undo日志和Read View的具体实现原理,帮助理解事务可见性的判定规则。

MVCC基本概念与作用

MVCC全称Multi-Version Concurrency Control,即多版本并发控制。这是一种并发控制的方法,广泛应用于数据库管理系统中,旨在实现对数据库的高并发访问。其核心目标是在减少读写冲突的同时,避免加锁带来的性能损耗,从而在不阻塞的情况下处理并发读写请求。

在MySQL InnoDB中,MVCC被视为行锁的一种高效变种。它允许读取操作在不获取排他锁或共享锁的情况下进行,显著提升了数据库的吞吐量。需要注意的是,MVCC并非完全替代行锁,而是与行锁配合工作,在不同场景下选择最优路径。

当前读与快照读的区别

理解MVCC的前提是区分两种不同的读取方式:当前读与快照读。这两者的根本差异在于是否读取数据的最新版本以及是否加锁。

第1步:识别当前读(Current Read)

定义与目的: 当前读指的是读取记录的最新一份版本,并且在读取过程中会对记录加锁,以保证其他并发事务不能修改该记录。 适用场景: 涉及数据修改或需要强一致性的操作。

  • SELECT ... LOCK IN SHARE MODE:加共享锁。
  • SELECT ... FOR UPDATE:加排他锁。
  • UPDATE, INSERT, DELETE:均属于当前读,因为它们会改变数据状态。

当前读确保了读取到的数据是“最新”且“稳定”的,适用于事务性较强的操作分支。

第2步:识别快照读(Snapshot Read)

定义与目的: 快照读是一种不加锁的非阻塞读操作。它读取的是数据的某个历史版本,而非最新版本的即时快照。这是提高数据库并发查询能力的关键手段。

  • 普通查询: 如简单的SELECT语句(不带FOR UPDATE或LOCK IN SHARE MODE),默认走快照读。
  • 隔离级别影响: 快照读的前提是事务隔离级别不是串行级(Serializable)。在串行模式下,快照读会退化为当前读,因为串行模式要求所有操作排队执行,无并发可言。

由于快照读读取的是历史版本,因此可能不会读到提交后的最新数据,但这正是MVCC提供事务隔离保证的基础。

MVCC的实现组件

MVCC模块在InnoDB中的具体实现依赖于三个核心组件:行记录的三个隐式字段、Undo日志以及Read View。它们共同协作,使得数据库能够追踪数据的变化历史并判断事务可见性。

第3步:理解行记录的隐式字段

除了用户自定义的业务字段外,InnoDB的行记录中还隐藏着以下三个关键字段:

  1. DB_TRX_ID:6字节,记录创建该行或最后一次修改该行的事务ID。这是一个递增的事务标识符。
  2. DB_ROLL_PTR:7字节,回滚指针。指向该行记录的上一个版本(即在Undo Log中的位置),用于链接版本链。
  3. DB_ROW_ID:6字节,隐藏主键。如果数据表未定义主键,InnoDB会自动生成这个唯一的Row ID作为聚簇索引的主键。

第4步:分析Undo日志的作用

Undo Log是MVCC实现“多版本”的基础。它在以下操作中产生:

  • INSERT:产生的Undo Log仅用于事务回滚,提交后即可丢弃。
  • UPDATE/DELETE:产生的Undo Log不仅用于回滚,还用于快照读。因此,这些日志不能被立即删除,直到确认没有任何活跃事务需要读取这些旧版本时,由专门的Purge线程统一清除。

当发生更新或删除时,InnoDB并不会物理删除旧数据,而是将旧版本的记录写入Undo Log,并通过DB_ROLL_PTR形成版本链。同时,在原记录上设置deleted_bit标记。只有当记录被标记为删除且不再有事务需要读取它时,才会被实际清理,以节省磁盘空间。

第5步:掌握Read View的机制

Read View(读视图)是事务在执行快照读的那一刻生成的系统当前快照。它记录了此时系统中活跃事务的ID列表、最小活跃事务ID和最大已分配事务ID等信息。

Read View决定了哪些版本的数据对当前事务可见。不同隔离级别的事务,其Read View的生成时机不同:

  • READ COMMITTED(RC):每次执行普通SELECT都生成一个新的Read View。
  • REPEATABLE READ(RR):仅在第一个SELECT语句时生成Read View,后续复用。

MVCC的可见性判定规则

MVCC的核心逻辑是判断一行数据对当前事务是否可见。这一过程结合了当前事务的Read View和数据行的隐式字段。

MVCC可见性判断流程

InnoDB在遍历版本链查找可见记录时,主要依据以下规则进行过滤:

  1. 比较事务ID: 将行记录的DB_TRX_ID与Read View中的活跃事务ID列表进行比较。
  2. 确定创建时间: 如果DB_TRX_ID小于Read View的最小活跃ID,说明该行数据在当前事务开始前已提交,可见。
  3. 确定删除时间: 如果DB_TRX_ID大于Read View的最大活跃ID,说明该行数据在当前事务开始后才会产生,不可见。
  4. 中间状态处理: 若DB_TRX_ID在最小和最大活跃ID之间,则检查该ID是否在活跃事务列表中。若在,说明修改该行的事务尚未提交,不可见;若不在,说明已提交,可见。

如图所示,在Transaction1中插入两条数据并提交。此时,Transaction2开启一个新事务进行读取。由于Transaction1已完成,其事务ID小于Transaction2当前的Read View阈值,因此Transaction2能正常读取到这两条新数据。

如图所示,在Transaction3中插入一条名为“老连”的数据,但该事务尚未提交。随后在Transaction2中进行读取。根据MVCC规则,Transaction3的ID处于Transaction2的活跃事务列表中(或大于其Read View阈值,取决于具体时机),因此Transaction2无法看到Transaction3中未提交的改动,只能查找到之前存在的那两条数据。

如图所示,在Transaction4中删除了ID为2的记录(假设ID为2的数据在Transaction2开始时存在),但Transaction4尚未提交。此时在Transaction2中读取。由于删除操作未生效(对Transaction2而言),且原记录的历史版本仍可通过Undo Log追溯,Transaction2依然能看到ID为2的那条旧数据,保持事务的一致性。

如图所示,演示更复杂的情况:Transaction5添加了新数据“涛哥”,删除了ID为1的数据,并将另一条数据修改为“涛哥”。在Transaction2中读取时,由于这些都是Transaction5在Transaction2启动后或未提交时的操作,Transaction2遵循其自身的Read View规则,忽略这些变更,仍然读取到自己启动时可见的数据集。

通过上述案例可以看出,MVCC通过版本号控制和版本链回溯,确保了即使在其他事务不断进行插入、修改或删除操作时,当前正在运行的事务所读取到的数据始终保持一致,不受外界干扰。

以上就是MySQL InnoDB MVCC多版本并发控制原理的详细内容,更多关于MySQL并发控制机制与事务隔离级别的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。