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

数据库MVCC

MVCC

一、MVCC 是什么?

MVCC 的全称是 Multi-Version Concurrency Control,即多版本并发控制

它是一种数据库管理技术,用于提高数据库在高并发场景下的性能。MVCC 通过在同一时刻保留数据行的多个版本,使得读操作(SELECT)不会阻塞写操作(UPDATE, DELETE),写操作也不会阻塞读操作。它完美解决了读写之间不必要的阻塞问题,极大地提升了并发能力。

MySQL 中 InnoDB 存储引擎的核心特性之一就是实现了 MVCC。

二、为什么需要 MVCC?—— 解决读写冲突

在没有 MVCC 的情况下,数据库通常通过来实现事务的隔离性。

  • 如果使用写锁(排他锁,X Lock),一个事务在写数据时,会阻塞其他事务的读和写。
  • 如果使用读锁(共享锁,S Lock),一个事务在读数据时,会阻塞其他事务的写,但允许其他事务读。

这种“锁”的方式虽然能保证数据的一致性,但并发性能很差,因为读和写是互斥的。

MVCC 提供了一种非锁的读(Consistent Nonlocking Read)方式。它让读操作去读一个快照版本,而写操作去创建一个新的版本。这样读和写操作的对象是不同的数据版本,因此可以并发执行,互不阻塞。

三、MVCC 的核心工作原理

MVCC 的实现依赖于三个核心概念一个关键机制

  1. 三个隐藏字段
  2. Undo Log(回滚日志)
  3. Read View(读视图)
1. 三个隐藏字段

InnoDB 为每一行数据都额外添加了三个用户看不到的隐藏字段:

  • DB_TRX_ID (6字节):最后修改该数据行的事务ID。记录是哪个事务插入或修改了这行数据。
  • DB_ROLL_PTR (7字节):回滚指针。指向该行数据在 Undo Log 中的上一个历史版本。它将所有版本的数据串联成一个版本链
  • DB_ROW_ID (6字节):行标识(隐藏主键)。如果表没有定义主键,InnoDB 会自动生成这个字段作为聚簇索引的键。
2. Undo Log (回滚日志)

Undo Log 保存了数据被修改前的旧版本数据。当执行 UPDATEDELETE 操作时,旧版本的数据并不会被立刻删除或覆盖,而是会被拷贝到 Undo Log 中。

  • 每修改一次,就会在 Undo Log 中生成一条记录。
  • 通过 DB_ROLL_PTR 回滚指针,可以将当前记录的所有历史版本串联起来,形成一个版本链。链头是最新的记录,链尾是最老的记录。
3. Read View (读视图) - MVCC 的“快照”灵魂

Read View事务在进行快照读(普通SELECT)时产生的,它定义了当前事务能看到哪个版本的数据。

Read View 本质上是一个数据结构,主要包含以下关键信息:

  • m_ids:生成 Read View 时,系统中活跃的(未提交的)读写事务ID的集合
  • min_trx_idm_ids 集合中的最小值。
  • max_trx_id:生成 Read View 时,系统应该分配给下一个事务的ID
  • creator_trx_id:创建这个 Read View当前事务的ID

数据可见性规则:
当访问某一行数据时,MVCC 会从最新的版本开始,顺着版本链依次判断每个版本是否对当前事务可见。判断规则如下:

  1. 如果被访问版本的 DB_TRX_ID 小于 min_trx_id,说明该版本在 Read View 创建前就已提交,对当前事务可见
  2. 如果被访问版本的 DB_TRX_ID 大于等于 max_trx_id,说明该版本在 Read View 创建后才生成,对当前事务不可见。需要顺着版本链继续找更老的版本。
  3. 如果被访问版本的 DB_TRX_IDmin_trx_idmax_trx_id 之间(min_trx_id <= trx_id < max_trx_id):
    • DB_TRX_ID m_ids(活跃事务集合),说明创建该版本的事务当时还未提交,该版本不可见
    • DB_TRX_ID 不在 m_ids ,说明创建该版本的事务当时已经提交,该版本可见
  4. 如果当前记录版本的 DB_TRX_ID 等于 creator_trx_id,说明是这个事务自己修改的记录,对自己总是可见的

一旦找到第一个对当前事务可见的版本,就返回这个版本的数据。

简单解释: Read View 就是 InnoDB 给每个快照读开的“时间戳发票”

事务拿到这张发票后,整个事务期间都按这张发票上的规则,判断到底能看哪个“历史版本”的行记录。

发票一旦打印,内容就不会再变,所以 repeatable read 才能做到“可重复”。

1.发票长什么样(4 个字段)

字段名含义类比
m_ids开票瞬间,还没提交的所有事务编号黑名单
min_trx_id黑名单里最小的那个编号最早“坏人”
max_trx_id系统下一个要分配的事务编号“未来人”起点
creator_trx_id开票人自己的事务编号我自己
  1. 拿到发票后怎么“验货”

对每条记录的每个版本(顺着 undo 链从头走到尾):

  1. 版本太老DB_TRX_ID < min_trx_id
    → 坏人名单里都没它,说明早就提交了,可见
  2. 版本太新DB_TRX_ID ≥ max_trx_id
    → 这是我开票之后才冒出来的,不可见,继续往 older 版本找。
  3. 版本在中间min_trx_id ≤ DB_TRX_ID < max_trx_id
    • 如果编号在黑名单 m_ids 里 → 开票时它还没提交,不可见
    • 如果编号不在黑名单 → 开票时它已提交,可见
  4. 版本是我自己改的DB_TRX_ID == creator_trx_id
    → 自己写的东西当然能看到,可见

一旦找到第一个“可见”版本就停下来返回,后面的 older 版本不再看。

  1. 一张图秒懂
时间轴:  ...[min_trx_id) ...[m_ids 黑名单]... [max_trx_id)...↑               ↑               ↑太老,可见      在黑名单→不可见   太新,不可见不在黑名单→可见
  1. 举个数字例子
  • 当前系统里活着的事务:88,90,93
  • 于是 Read View:
    m_ids = {88,90,93}
    min_trx_id = 88
    max_trx_id = 95(系统下一个号)
    creator_trx_id = 91(我自己)

来一条记录版本链:

版本号(DB_TRX_ID)判断可见?
96≥ max_trx_id
9488≤94<95 且 94∉m_ids可见 (返回)
  1. 一句话总结

Read View 把“并发世界”瞬间拍成一张静态照片,之后事务无论读多少次,都只看这张照片允许的“历史镜像”,从而

  • 挡住未提交的脏数据(脏读)
  • 挡住已提交的后续改动(不可重复读)
  • 配合间隙锁还能挡住新插入的幻影行(幻读)

这就是 MVCC 里“快照”真正的灵魂。

四、MVCC 如何实现不同隔离级别?

MVCC 主要作用于 READ COMMITTED (RC,提交读)REPEATABLE READ (RR,可重复读) 这两个隔离级别。

  • READ COMMITTED (RC)
    • 核心:每次执行快照读(SELECT)时,都会生成一个新的 Read View
    • 效果:每次读都能看到最新已经提交的事务所做的修改。所以会出现“不可重复读”现象(同一个事务内两次读取同一数据,结果可能不同)。
  • REPEATABLE READ (RR)
    • 核心:只在第一次执行快照读时生成一个 Read View,后续所有的读操作都复用这个 Read View
    • 效果:在整个事务期间,每次读到的数据都是一致的,就像是在事务开始时拍了一个快照一样。因此解决了“不可重复读”问题。(这也是 InnoDB 在 RR 级别下能防止幻读的手段之一)。

五、一个简单的例子

假设:

  • 事务A (id=10) 开启,查询一条记录。
  • 事务B (id=20) 修改了这条记录并提交。
  • 事务A 再次查询。

在 RC 级别下:

  1. 事务A第一次查询,生成 Read View1m_ids 包含 [10](假设只有自己活跃)。它读到的是原始版本。
  2. 事务B修改并提交。
  3. 事务A第二次查询,生成一个新的 Read View2,此时 m_ids 只包含 [10](20已提交)。根据规则,它能看到事务B提交的版本。所以两次查询结果不同(不可重复读)。

在 RR 级别下:

  1. 事务A第一次查询,生成 Read View1m_ids 包含 [10]。
  2. 事务B修改并提交。
  3. 事务A第二次查询,复用之前的 Read View1。根据规则,Read View1 生成时,事务B (20) 还未开始或处于活跃状态(取决于时机),所以事务B修改的版本对 Read View1 不可见。事务A只能看到和第一次一样的原始版本。所以两次查询结果相同(可重复读)。

六、总结

特性说明
目的提高并发性能,实现读写不阻塞
实现基础隐藏字段 (DB_TRX_ID, DB_ROLL_PTR) + Undo Log (版本链) + Read View (可见性判断)。
核心思想为每个事务提供一个数据快照,读操作读历史版本,写操作创建新版本。
适用操作快照读(普通 SELECT ... 不加锁)。当前读SELECT ... FOR UPDATE, UPDATE, DELETE, INSERT 会加锁)不适用。
与隔离级别关系RCRR 隔离级别实现的基础。RC 每次读生成新 Read ViewRR 第一次读生成 Read View 并复用。
优点读不加锁,读写不冲突,并发性能高。
缺点需要维护多版本数据,会占用更多的存储空间;需要复杂的垃圾回收机制来清理不再需要的旧版本数据。

简单来说,MVCC 就是通过给数据行“拍快照” 的方式,让每个事务都能看到一份一致的数据视图,从而巧妙地避免了不必要的锁竞争,是现代数据库实现高并发的重要技术。

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

相关文章:

  • 如何用AI工具开发一个轻量化CRM系统(七):AI生成pytest测试脚本
  • qData:一站式开源数据中台
  • 国外中文网站排行在线图片编辑网站源码
  • [数据结构]优先级队列
  • ARM内部寄存器
  • Laravel + UniApp AES加密/解密
  • 5G开户时切片配置参数详解
  • 面向新质生产力,职业院校“人工智能”课程教学解决方案
  • wap网站如何做福建外贸网站
  • ElasticSearch-提高篇
  • 第6篇、Flask 表单处理与用户认证完全指南:从零到实战
  • Visual Studio 2013 Update 4 中文版安装步骤(带TFS支持)附安装包​
  • 珠海 网站建设注册安全工程师题库
  • 上手 cpp-httplib:轻量级 C++ HTTP 库的安装与实战指南
  • 突破文档型数据库迁移困境:金仓多模方案破解电子证照系统国产化难题
  • 网站手机客户端开发wordpress制造商单页
  • Net 》》C# 》》try finally 执行顺序
  • 在 Unity 项目中使用 FFMpeg 进行音频转码(WAV 转 MP3)
  • 使用Java将Word文件转换为PNG图片
  • 如何用Fail2ban保护Linux服务器?防止SSH暴力破解教程
  • 开源 C# 快速开发(五)自定义控件--仪表盘
  • 华为FreeClip 2耳夹耳机:让「戴着不摘」成为新的使用习惯
  • 算法继续刷起-2025年09月26日
  • AI笔记在学习与工作中的高效运用
  • QML学习笔记(十四)QML的自定义模块
  • ubuntu一键安装vscode: 使用官方 APT 仓库
  • python做网站的 框架企业邮箱什么样子
  • 学习游戏制作记录(爆炸敌人的制作)
  • 第三方软件登记测试机构:【软件登记测试机构HTML5测试技术】
  • 元宇宙的宠物经济:虚拟宠物的饲养与交易