0

0

多版本并发控制(MVCC)在InnoDB中的实现原理剖析

夜晨

夜晨

发布时间:2025-09-09 12:02:01

|

473人浏览过

|

来源于php中文网

原创

MVCC通过保存数据多版本解决并发读写冲突,InnoDB利用隐藏列DB_TRX_ID、DB_ROLL_PTR和Undo Log实现该机制;事务读取时依据Read View判断数据可见性,RC隔离级别每次查询生成新Read View,RR级别仅在首次读取时创建并复用;Undo Log不仅支持事务回滚,还存储历史版本供MVCC使用,通过purge线程异步清理过期版本以释放空间。

多版本并发控制(mvcc)在innodb中的实现原理剖析

多版本并发控制(MVCC)在InnoDB中,简单来说,就是一种通过保存数据多个历史版本来解决并发读写冲突的机制。它允许事务在不互相阻塞的情况下,看到数据的一个“快照”,从而提高数据库的并发性能和数据一致性。在我看来,这简直是数据库并发控制领域的一个里程碑式设计,它巧妙地平衡了性能与数据完整性。

解决方案

InnoDB实现MVCC的核心,在于为每一行数据都维护了几个隐藏的列,以及一个至关重要的“Undo Log”系统。当一行数据被修改时,InnoDB并不会直接覆盖旧数据,而是会创建一个新的数据版本,并将旧版本的数据信息存入Undo Log。

具体来说,每一行记录都包含了以下几个关键的隐藏字段:

  • DB_TRX_ID:记录了最近一次修改该行的事务ID。
  • DB_ROLL_PTR:一个回滚指针,指向Undo Log中该行上一个版本的记录。通过这个指针,可以串联起一个行的所有历史版本。
  • DB_ROW_ID:一个隐含的行ID,当表没有主键或唯一索引时,InnoDB会使用这个ID来唯一标识一行。

当一个事务需要读取数据时,它会根据自身的“Read View”(一个由当前活跃事务ID列表组成的快照)来判断应该看到哪个版本的数据。如果当前行的

DB_TRX_ID
在Read View的活跃事务列表中,或者比Read View中最小的活跃事务ID还要新,那么该行对当前事务就是不可见的。此时,InnoDB会沿着
DB_ROLL_PTR
指针,从Undo Log中获取更早的数据版本,直到找到一个对当前事务可见的版本。

这种设计使得读操作(SELECT)通常不需要加锁,因为它总能找到一个合适的历史版本来读取,避免了读写之间的阻塞。写操作(INSERT, UPDATE, DELETE)则会创建新的数据版本或标记旧版本,并将旧版本信息推入Undo Log,保证了数据的持久性和可回滚性。

MVCC 如何解决并发读写冲突?

在我看来,MVCC解决并发读写冲突的精髓在于它的“非阻塞性”和“快照隔离”。传统的锁机制,比如共享锁和排他锁,在读写冲突时往往会导致事务等待,降低了并发度。但MVCC则完全不同。

当一个事务A正在修改一行数据时,事务B如果尝试读取同一行,它并不会被事务A的写操作阻塞。相反,事务B会利用MVCC机制,通过回滚指针(DB_ROLL_PTR)和Undo Log,回溯到该行数据在事务A开始修改之前的某个版本(具体是哪个版本取决于事务B的隔离级别和Read View的生成时机)。这样,事务B就能看到一个一致性的、未被修改的“快照”数据,而事务A也能继续它的修改操作,两者互不干扰。

说白了,MVCC就是用“空间换时间”的策略。它通过存储数据的多个版本(占用额外的存储空间,主要是Undo Log),避免了读写操作之间的互相等待(节省了时间,提高了并发性)。这种设计在OLTP(在线事务处理)系统中尤其重要,因为这类系统通常有大量的并发读写请求,对响应速度和吞吐量要求极高。当然,这种方式也引入了额外的管理开销,比如旧版本的清理,但总体而言,收益远大于成本。

InnoDB MVCC 中的 Read View 是什么,以及它如何工作?

Read View,在我看来,是InnoDB MVCC机制中一个非常巧妙且核心的概念,它是决定一个事务能看到哪个数据版本的“眼睛”。每个事务在启动时(或者在某些隔离级别下,每次执行查询时)都会生成一个Read View。

一个Read View 主要包含以下几个信息:

  1. m_ids:一个活跃的事务ID列表,表示在生成Read View时,所有正在活跃(还未提交或回滚)的事务ID。
  2. min_trx_id:m_ids列表中最小的事务ID,或者说,在生成Read View时,所有活跃事务中最早的那个事务ID。
  3. max_trx_id:在生成Read View时,系统下一个将要分配的事务ID,它比当前所有已分配的事务ID都大。

当一个事务要读取一行数据时,InnoDB会获取该行记录的

DB_TRX_ID
,然后根据这个
DB_TRX_ID
和当前事务的Read View进行一系列判断:

Red Panda AI
Red Panda AI

AI文本生成图像

下载
  • 如果行的
    DB_TRX_ID
    小于
    min_trx_id
    :这表示该行是在当前Read View生成之前就已经提交的事务修改的,因此该行对当前事务是可见的。
  • 如果行的
    DB_TRX_ID
    大于等于
    max_trx_id
    :这表示该行是由在当前Read View生成之后才启动的事务修改的,因此该行对当前事务是不可见的。
  • 如果行的
    DB_TRX_ID
    min_trx_id
    max_trx_id
    之间
    • 如果
      DB_TRX_ID
      m_ids
      列表中
      :这表示该行是由一个在Read View生成时仍然活跃的事务修改的。除非这个事务就是当前查询的事务本身(自己的修改自己可见),否则该行对当前事务是不可见的。
    • 如果
      DB_TRX_ID
      不在
      m_ids
      列表中
      :这表示该行是由一个在Read View生成时已经提交的事务修改的,因此该行对当前事务是可见的。

如果当前版本不可见,InnoDB就会沿着

DB_ROLL_PTR
指针,从Undo Log中获取上一个历史版本,并重复上述判断过程,直到找到一个可见的版本。

这里值得一提的是,不同的事务隔离级别对Read View的生成时机有影响:

  • READ COMMITTED (RC):每次查询(SELECT语句)都会生成一个新的Read View。这意味着在同一个事务中,两次查询可能会看到不同的数据快照,因为在两次查询之间,可能有其他事务提交了。
  • REPEATABLE READ (RR):事务的Read View只在事务第一次读取数据时生成,并在整个事务生命周期内保持不变。这保证了在同一个事务中,无论何时读取,都能看到相同的数据快照,从而实现“可重复读”。这也是为什么RR是InnoDB的默认隔离级别。

理解Read View的工作原理,是深入理解MVCC的关键,它直接决定了事务之间如何“看”到数据,以及如何实现不同程度的隔离。

Undo Log 在 MVCC 实现中扮演了什么角色?

Undo Log,在我看来,是InnoDB MVCC实现中不可或缺的基石,它不仅用于事务回滚,更是MVCC能够提供多版本并发控制的“时间机器”。没有Undo Log,MVCC就无从谈起。

它的主要作用体现在两个方面:

  1. 事务回滚:这是Undo Log最直接、最容易理解的功能。当一个事务需要回滚时(无论是用户显式回滚,还是系统崩溃导致自动回滚),InnoDB会利用Undo Log中记录的操作,将所有已做的修改“撤销”,使数据恢复到事务开始之前的状态。每当修改数据时,InnoDB都会将修改前的数据状态记录在Undo Log中,形成一个链条。

  2. 实现MVCC:这是Undo Log更深层次、更巧妙的作用。正如前面提到的,当一个事务修改一行数据时,它并不会直接覆盖旧数据。相反,它会创建一个新的数据版本,并将旧数据版本的信息(包括旧的

    DB_TRX_ID
    DB_ROLL_PTR
    指向的旧版本)写入到Undo Log中。这个写入到Undo Log的旧版本,就是MVCC机制中供其他事务读取的“历史版本”。

    想象一下,Undo Log就像是一个数据的“版本历史记录本”。每当数据发生修改,旧的版本就被“存档”到这个本子里,并且通过

    DB_ROLL_PTR
    形成一个链表,从最新版本一直回溯到最原始的版本。当一个事务需要读取数据时,如果最新版本对它不可见,它就沿着
    DB_ROLL_PTR
    这条线,一步步地“倒带”到Undo Log中,查找那个对它可见的历史版本。

Undo Log的生命周期也很有趣。一旦一个事务提交,它所产生的Undo Log记录并不会立即被删除。这些记录必须保留,直到所有可能需要读取这些旧版本的活跃事务都完成为止。这个清理过程通常由一个后台线程(purge线程)异步完成,它会定期扫描Undo Log,清理那些不再被任何活跃事务引用的旧版本记录,以回收存储空间。如果Undo Log增长过快,或者purge线程跟不上,可能会导致数据库性能下降,甚至存储空间耗尽,这也是为什么Undo Log的管理和监控非常重要。

可以说,Undo Log是InnoDB实现ACID特性中“原子性”和MVCC“隔离性”的关键技术支撑。它不仅仅是事务的撤销日志,更是构建多版本数据视图的基石。

相关专题

更多
线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

469

2023.08.10

数据库Delete用法
数据库Delete用法

数据库Delete用法:1、删除单条记录;2、删除多条记录;3、删除所有记录;4、删除特定条件的记录。更多关于数据库Delete的内容,大家可以访问下面的文章。

266

2023.11.13

drop和delete的区别
drop和delete的区别

drop和delete的区别:1、功能与用途;2、操作对象;3、可逆性;4、空间释放;5、执行速度与效率;6、与其他命令的交互;7、影响的持久性;8、语法和执行;9、触发器与约束;10、事务处理。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

206

2023.12.29

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

330

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2068

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

346

2023.08.31

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

251

2023.09.05

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

319

2023.10.09

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

0

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Node.js 教程
Node.js 教程

共57课时 | 7.7万人学习

CSS3 教程
CSS3 教程

共18课时 | 4.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号