当前位置: 首页 > news >正文

MVCC 多版本并发控制 详解

目录

为什么需要MVCC?

MVCC的核心组件

1. 隐藏字段

2. undo log(回滚日志)

3. Read View(读视图)

MVCC的可见性判断规则

不同隔离级别的MVCC表现

示例:MVCC如何工作?

MVCC的优势

注意事项


MVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库中用于处理并发访问的核心机制,其核心目标是在读写操作不冲突的前提下,保证事务的隔离性,同时提升数据库的并发性能。

为什么需要MVCC?

在并发场景中,传统的锁机制(如读锁、写锁)会导致读写操作互相阻塞:

  • 读操作持有读锁时,写操作需要等待读锁释放才能修改数据;
  • 写操作持有写锁时,读操作需要等待写锁释放才能读取数据。

这种阻塞会严重影响数据库的并发效率。而MVCC通过保留数据的历史版本,让读操作可以直接读取历史版本,无需等待写操作释放锁;同时写操作也无需阻塞读操作,从而大幅提升并发性能。

MVCC的核心组件

MVCC的实现依赖三个关键部分:隐藏字段undo logRead View,三者协同工作形成数据的多版本管理和可见性判断机制。

1. 隐藏字段

InnoDB中,每张表的每行记录除了用户定义的字段外,还隐含三个隐藏字段(用于版本管理):

  • DB_TRX_ID:记录最后一次修改该记录的事务ID(6字节)。事务开启时,InnoDB会为其分配一个唯一的递增事务ID。
  • DB_ROLL_PTR:回滚指针(7字节),指向当前记录的上一个版本(存储在undo log中),形成“版本链”。
  • DB_ROW_ID:行ID(6字节,可选)。如果表没有主键或唯一索引,InnoDB会用该字段作为聚簇索引的键。

2. undo log(回滚日志)

当事务修改数据时,InnoDB会生成undo log,记录数据修改前的状态(用于回滚或供读操作访问历史版本)。

  • 例如:事务T1将一条记录的name从“张三”改为“李四”,则undo log会记录“name=张三”的旧版本,同时当前记录的DB_ROLL_PTR指向这条undo log。
  • 多次修改后,undo log会通过DB_ROLL_PTR串联成版本链(最新版本在表中,旧版本在undo log中)。

版本链示例:

当前记录(最新版本)
DB_TRX_ID = 100(最后修改的事务ID)
DB_ROLL_PTR → undo log 1(版本1:name=张三,DB_TRX_ID=50)↓undo log 2(版本2:name=王五,DB_TRX_ID=30)

3. Read View(读视图)

Read View是事务在读取数据时生成的“快照”,用于判断哪个版本的记录对当前事务可见。它包含4个核心属性:

  • m_ids:当前活跃的事务ID列表(即尚未提交的事务ID)。
  • min_trx_id:m_ids中的最小事务ID(当前活跃事务的最小ID)。
  • max_trx_id:下一个将要分配的事务ID(并非m_ids中的最大值,而是全局事务ID的下一个值)。
  • creator_trx_id:生成该Read View的事务ID。

MVCC的可见性判断规则

当事务读取一条记录时,会从版本链的最新版本开始,依次通过Read View判断每个版本的DB_TRX_ID是否可见。规则如下:

  1. 若当前版本的DB_TRX_ID = creator_trx_id
    说明该版本是当前事务自己修改的,可见。
  2. DB_TRX_ID < min_trx_id
    说明修改该版本的事务在当前Read View生成前已提交,可见。
  3. DB_TRX_ID > max_trx_id
    说明修改该版本的事务在当前Read View生成后才开启,不可见(需通过DB_ROLL_PTR找前一个版本)。
  4. min_trx_idDB_TRX_IDmax_trx_id
    • DB_TRX_IDm_ids中(即事务仍活跃):不可见,找前一个版本;
    • DB_TRX_ID不在m_ids中(即事务已提交):可见。

不同隔离级别的MVCC表现

MVCC的行为会因事务隔离级别不同而变化,主要影响Read View的生成时机:

隔离级别

Read View生成时机

效果

读已提交(RC)

每次查询时生成新的Read View

可能读取到其他事务已提交的新数据(解决脏读,但可能出现不可重复读)。

可重复读(RR)

事务第一次查询时生成Read View,全程复用

多次查询结果一致(解决不可重复读,InnoDB默认隔离级别)。

示例:MVCC如何工作?

假设初始数据:id=1, name=张三DB_TRX_ID=0,表示未被事务修改)。

  1. 事务T1(ID=10)修改数据
    • name改为“李四”,生成undo log(记录旧值“张三”)。
    • 当前记录DB_TRX_ID=10DB_ROLL_PTR指向undo log(旧版本)。
  1. 事务T2(ID=20)读取数据
    • T2生成Read View:m_ids=[10](T1未提交),min_trx_id=10max_trx_id=30creator_trx_id=20
    • 检查最新版本DB_TRX_ID=10:在m_ids中(T1活跃),不可见。
    • 通过DB_ROLL_PTR找到旧版本(DB_TRX_ID=0):0 < min_trx_id=10,可见,因此T2读取到“张三”。
  1. T1提交后,T2再次查询(RC级别)
    • RC级别会生成新Read View:m_ids=[](T1已提交),min_trx_id=30max_trx_id=30
    • 最新版本DB_TRX_ID=10 < min_trx_id=30,可见,因此T2读取到“李四”(不可重复读)。
  1. 若T2是RR级别
    • 全程复用第一次生成的Read View(m_ids=[10]),即使T1提交,T2仍读取到“张三”(可重复读)。

MVCC的优势

  • 读写不冲突:读操作无需加锁,直接读取历史版本;写操作仅锁定当前版本,不阻塞读。
  • 提升并发性能:避免了传统锁机制的阻塞,支持高并发读写。
  • 简化隔离级别实现:通过Read View的生成时机,轻松实现RC和RR隔离级别。

注意事项

  • MVCC仅解决读-写冲突写-写冲突仍需行锁(如InnoDB的Record Lock)解决。
  • undo log会占用空间,InnoDB的purge线程会定期清理“所有Read View都无法访问”的旧版本undo log。

总结:MVCC通过版本链、undo log和Read View的协同,实现了“读不加锁、读写并行”,是InnoDB高效支持并发事务的核心机制。

http://www.dtcms.com/a/291316.html

相关文章:

  • C语言(20250721)
  • 【PTA数据结构 | C语言版】验证六度空间理论
  • day20-sed-find
  • 【学习路线】C#企业级开发之路:从基础语法到云原生应用
  • 感知机-梯度下降法
  • 代码随想录day41dp8
  • 教资科三【信息技术】— 学科知识: 第三章(多媒体技术)
  • Java I/O模型深度解析:BIO、NIO与AIO的演进之路
  • CDN和DNS 在分布式系统中的作用
  • JAVA+AI教程-第三天
  • 数据库mysql是一个软件吗?
  • 主流 MQ 的关键性能指标
  • 瑶池数据库Data+AI驱动的全栈智能实践开放日回顾
  • 5.Java的4个权限修饰符
  • 如何用 LUKS 和 cryptsetup 为 Linux 配置加密
  • 3.4 递归函数
  • GUI简介
  • CMake变量和环境变量之间的关系和区别CMAKE_EXPORT_COMPILE_COMMANDS环境变量作用
  • Weex 知识点
  • SymPy 中抽象函数求导与具体函数代入的深度解析
  • C多线程下的fwrite与write:深入挖掘与实战指南
  • 每日算法刷题Day51:7.21:leetcode 栈6道题,用时1h40min
  • 【项目实战】——深度学习.全连接神经网络
  • PostgreSQL SysCache RelCache
  • Java API (二):从 Object 类到正则表达式的核心详解
  • DevOps是什么?
  • Flutter中 Provider 的基础用法超详细讲解(一)
  • C++的“链”珠妙笔:list的编程艺术
  • JAVA序列化知识小结
  • mac终端设置代理