MVCC 底层原理详解

大家好 我是积极向上的湘锅锅💪💪💪

基本概念

当前读

顾名思义,读取的就是当前最新的版本,而且读取的时候保证其他并发事务不会修改该记录,会对记录加锁(共享锁或者是排它锁),如:select … lock in share mode(共享锁),select …
for update、update、insert、delete(排他锁)都是一种当前读

快照读

简单的 select(不加锁)就是快照读,快照读读取的可能是最新版本,也有可能是历史数据

Read Committed:每个select都会产生一个快照读

Repeatable Read:开启事务后第一个select才是快照读的地方

Serializable:快照读会退化为当前读(加锁)

MVCC

全称Multi-Version Concurrency Control,多版本并发控制,指维护一个数据的多个版本,使得读写操作没有冲突,也就是不会产生读写锁冲突,而快照读为MySQL实现MVCC提供了一个非阻塞读功能,这给我们高并发性能提升不少

MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView

接下来,我们再来介绍一下InnoDB引擎的表中涉及到的隐藏字段 、undolog 以及 readview,从
而来介绍一下MVCC的原理。

隐藏字段

在这里插入图片描述
举个例子当我们创建了这张表,在查看表结构的时候,只看得到这三个显示的字段,但是实际上除了这三个字段意外,InnoDB还会自动的给我们添加三个隐藏字段,如下:
在这里插入图片描述
而上面前俩个字段是肯定添加的,而第三个要看是否自己设置了主键,如果没有指定主键,则会添加该字段

undolog

回滚日志,在insert,update,delete的时候产生便于数据回滚的日志

  • 在insert的时候,产生的undo log 日志旨在回滚的时候需要,在事务提交后,可被立即删除
  • 而update,delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除

版本链

为什么叫版本链,慢慢往下看…

有一张表的原始数据为:
在这里插入图片描述

DB_TRX_ID : 代表最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID,是自增的

DB_ROLL_PTR : 指向上一个版本,由于这条数据是才插入的,没有被更新过,所以该字段值为null。

然后,有四个并发事务同时在访问这张表

第一步

在这里插入图片描述
当事务2开始修改数据时,会产生undo log日志,记录数据变更之前的样子,并且会记录本次操作的事务id
在这里插入图片描述

第二步

在这里插入图片描述
当事务3执行第一条修改语句时,也会记录记录undo log日志,记录数据变更之前的样子,然后更新版本记录,并且记录本次操作事务id
在这里插入图片描述

第三步

在这里插入图片描述
当事务4执行第一条修改语句时,也会记录undo log日志,记录数据变更之前的样子; 然后更新记
录,并且记录本次操作的事务ID
在这里插入图片描述
最终我们发现,不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条
记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录

Readview

Readview(读视图)是快照读SQL执行的时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id
Redaview中包含了四个核心字段:
在这里插入图片描述
看图有点抽象,我觉得可以这样理解

  • m_ids:当前活跃的事务ID集合 ==>当前并发过程中还未提交的事务ID集合
  • min_trx_id:最小活跃事务ID ==>在还未提交的事务ID集合中最小那个
  • max_trx_id:预分配事务ID,当前最大事务ID+1 ==>也就是说下一个事务ID应该是当前还未提交的事务id集合中最大那个ID+1
  • creator_trx_id:ReadView创建者的事务ID ==>这个好理解 就是哪个事务创建的这个ReadView那就是那个事务的ID
  • 如果还有疑问下面还有图帮助理解哦,觉得有帮助请点个赞

而在readview中就规定了版本链数据的访问规则
trx_id 代表当前undolog版本链对应事务ID
在这里插入图片描述
看这可能有的小伙伴有点懵 我对这张图解释一下,不对之处请指出

  • trx_id 可以理解为每一条匹配的undo log版本链的事务ID,也就是哪个事务ID产生的这条记录
  • 是从版本链尾部到头部开始匹配
  • 如果trx_id == creator_trx_id那就说明改记录是自己所在的事务更改的,是一家人,那肯定可以访问该版本
  • 如果trx_id < min_trx_id,也就是说访问的这个事务已经提交了,因为目前还未结束的事务ID都比它大,那就可以访问该版本
  • 如果trx_id > max_trx_id,那就说明已经有事务在生成ReadView之后开启了,我们肯定不能访问该版本,防止产生重复读的问题
  • 如果min_trx_id <= trx_id <=max_trx_id,如果trx_id不在m_ids集合里面,那就说明该事务已经提交了,提交了我们肯定可以访问,如果没有提交,那就不能访问

不同的隔离级别,生成ReadView的时机不同

  • READ COMMITTED :在事务中每一次执行快照读时生成ReadView。
  • READ REPEATABLE:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。

原理分析

1.RC隔离级别

RC隔离级别下,在事务中每一次执行快照读时生成ReadView。

我们就来分析事务5中,两次快照读读取数据,是如何获取数据的?
在事务5中,查询了两次id为30的记录,由于隔离级别为Read Committed,所以每一次进行快照读
都会生成一个ReadView,那么两次生成的ReadView如下:
在这里插入图片描述
这里有图也可以好好理解一下ReadView每一个字段所代表的具体含义
那么俩次快照读在获取数据的时候,就需要根据所生成的ReadView和ReadView的版本链访问规则,到undo log版本练中匹配数据,最终决定此次快照读使用哪一条记录的数据

A.先来看第一次快照读具体的读取过程
在这里插入图片描述
下图是版本链和访问规则
在这里插入图片描述在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:

  • 先匹配这条记录在这里插入图片描述
    这条记录对应的trx_id为4,也就是将4带入右侧的匹配规则中
    ①不满足 ②不满足 ③不满足 ④也不满足 ,
    都不满足,则继续匹配undo log版本链的下一条

  • 再匹配第二条在这里插入图片描述
    这条记录对应的trx_id为3,也就是将3带入右侧的匹配规则中。
    ①不满足 ②不满足 ③不满足 ④也不满足
    都不满足,则继续匹配undo log版本链的下一条

  • 再匹配第三条在这里插入图片描述
    这条记录对应的trx_id为2,也就是将2带入右侧的匹配规则中。
    ①不满足 ②满足
    终止匹配
    此次快照读,返回的数据就是版本链中trx_id为2记录的这条数据

B. 再来看第二次快照读具体的读取过程
在这里插入图片描述
上图中注意第二次快照读是下面那个ReadView
在这里插入图片描述

  • 先匹配这条记录
    在这里插入图片描述
    这条记录对应的trx_id为4,也就是将4带入右侧的匹配规则中
    ①不满足 ②不满足 ③不满足 ④也不满足
    都不满足,则继续匹配undo log版本链的下一条

  • 再匹配第二条
    在这里插入图片描述
    这条记录对应的trx_id为3,也就是将3带入右侧的匹配规则中。
    ①不满足 ②满足
    终止匹配
    此次快照读,返回的数据就是版本链中记录的这条数据

2. RR隔离级别

RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。 而RR 是可
重复读,在一个事务中,执行两次相同的select语句,查询到的结果是一样的
那在MVCC的机制下,是如何防止可重复读的呢?
还是用事务5举例
在这里插入图片描述
我们看到,在RR隔离级别下,只是在事务中第一次快照读时生成ReadView,后续都是复用该
ReadView,那么既然ReadView都一样, ReadView的版本链匹配规则也一样, 那么最终快照读返
回的结果也是一样的

结论

所以呢,MVCC的实现原理就是通过 InnoDB表的隐藏字段、UndoLog 版本链、ReadView来实现的,而MVCC + 锁(next-key ),则实现了事务的隔离性。 而一致性则是由redolog 与 undolog保证

<think>好的,用户想了解MVCC的实现原理,需要详细解释。我需要结合之前提供的引用资料来组织回答。首先,确定MVCC的基本概念,然后分点说明实现机制,比如隐式字段、undo log、Read View等。还要注意引用标注的正确性,确保每个部分都有对应的引用来源。同时,要检查是否有用户不可见的系统级指令需要遵守,比如LaTeX格式和结构要求。可能还需要生成相关问题,但放在最后。用户可能对事务隔离级别和MVCC的关系不太清楚,所以需要明确说明。另外,需要对比当前读和快照读,这也是用户可能感兴趣的点。确保回答结构清晰,分步骤讲解,避免技术术语堆砌,让用户容易理解。</think>### MVCC实现原理详解 MVCC(多版本并发控制)是数据库管理系统中实现非阻塞读操作的核心技术,通过数据多版本管理实现读写操作并发执行。其实现依赖三个核心组件: #### 一、基础存储结构 1. **隐式字段** 每条记录包含三个系统字段(用户不可见): - `DB_TRX_ID`(6字节):记录最后一次修改该数据的事务ID $$示例值:事务100修改后的数据\ DB\_TRX\_ID=100$$ - `DB_ROLL_PTR`(7字节):指向undo log的回滚指针[^3] - `DB_ROW_ID`(6字节):隐藏自增主键(当表无主键时生成)[^2] 2. **版本链构建** 通过回滚指针形成的版本链结构: ```plaintext 当前记录 ← undo log记录v3 ← undo log记录v2 ← undo log记录v1 ``` #### 二、核心运行机制 1. **Read View生成** 事务首次读操作时生成包含四个要素的读视图: - `creator_trx_id`:当前事务ID - `m_ids`:活跃事务ID集合 - `min_trx_id`:最小活跃事务ID - `max_trx_id`:预分配的下个事务ID[^4] 2. **可见性判断算法** 按顺序检查每条记录的版本链: ``` if trx_id == creator_trx_id → 可见(当前事务修改) elif trx_id < min_trx_id → 可见(已提交事务) elif trx_id >= max_trx_id → 不可见(未来事务) elif trx_id in m_ids → 不可见(活跃事务) else → 可见(已提交事务) ``` #### 三、版本控制实现 1. **数据更新流程** - 插入新记录时生成新版本 - 更新操作会创建新版本并更新回滚指针 - 删除操作标记为删除状态[^5] 2. **清理机制** 通过`purge`线程定期清理不再需要的旧版本数据,清理条件: $$事务ID < 当前最旧活跃事务ID\ \&\&\ 无Read View依赖$$ #### 四、与事务隔离的对应关系 | 隔离级别 | 快照生成时机 | 可见性范围 | |----------------|----------------------|---------------------| | 读未提交 | 不使用MVCC | 直接读取最新数据 | | 读已提交(RC) | 每次查询生成新快照 | 已提交事务的最新版 | | 可重复读(RR) | 首次查询生成固定快照 | 事务开始时的数据版 | | 串行化 | 使用锁机制 | 无并发访问 | #### 五、读写操作类型 1. **快照读** ```sql SELECT * FROM table; -- 使用Read View读取可见版本[^2] ``` 2. **当前读** ```sql SELECT * FOR UPDATE; -- 直接读取最新数据并加锁[^3] ``` [^1]: 事务隔离机制与MVCC的关系说明 [^2]: 快照读与当前读的对比分析 [^3]: 版本链构建过程的技术实现 [^4]: Read View生成规则详述 [^5]: 数据清理机制实现原理
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

owensweat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值