MySQL InnoDB MVCC多版本并发控制原理详解
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的行记录中还隐藏着以下三个关键字段:
DB_TRX_ID:6字节,记录创建该行或最后一次修改该行的事务ID。这是一个递增的事务标识符。DB_ROLL_PTR:7字节,回滚指针。指向该行记录的上一个版本(即在Undo Log中的位置),用于链接版本链。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在遍历版本链查找可见记录时,主要依据以下规则进行过滤:
- 比较事务ID: 将行记录的
DB_TRX_ID与Read View中的活跃事务ID列表进行比较。 - 确定创建时间: 如果
DB_TRX_ID小于Read View的最小活跃ID,说明该行数据在当前事务开始前已提交,可见。 - 确定删除时间: 如果
DB_TRX_ID大于Read View的最大活跃ID,说明该行数据在当前事务开始后才会产生,不可见。 - 中间状态处理: 若
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并发控制机制与事务隔离级别的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









