【MySQL】MVCC及其实现原理

news2024/9/27 5:20:55

目录

1. 概念介绍

什么是MVCC

什么是当前读和快照读

MVCC的好处

2. MVCC实现原理

隐藏字段

Read View

undo-log

数据可见性算法

3. RC和RR隔离级别下MVCC的差异

4. MVCC+Next-key-Lock 防止幻读


1. 概念介绍

什么是MVCC

Multi-Version Concurrency Control 多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。

                                                                                                         MVCC_百度百科

MVCC允许多个事务同时读取同一行数据,而不会彼此阻塞,每个事务看到的数据版本是该事务开始时的数据版本。这意味着,如果其他事务在此期间修改了数据,正在运行的事务仍然看到的是它开始时的数据状态,从而实现了非阻塞读操作。


什么是当前读和快照读

先来介绍一下MySQL下InnoDB索引的当前读和快照读:

  • 当前读(锁定读)

像 select lock in share mode (共享锁), select for update; update; insert; delete (排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁

  • 快照读(一致性非锁定读)

不加锁的 select 操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即 MVCC ,可以认为 MVCC 是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本

当前读是指读取数据时获取最新版本,并通过加锁确保其他事务无法修改该记录,以保证一致性。
快照读则是在不加锁的情况下读取数据,基于MVCC,可以访问历史版本,从而提高并发性能,但可能不获取最新数据。


MVCC的好处

  1. 提高并发性:通过允许多个事务同时访问不同版本的数据,减少了锁的竞争,从而提高了并发性能。

  2. 降低阻塞:读操作不需要等待写操作完成,减少了事务之间的阻塞,提升了响应速度。

  3. 保证一致性:每个事务都可以读取到一个一致的数据快照,避免了因并发更新导致的数据不一致问题。

  4. 性能优化:在许多情况下,MVCC可以避免加锁操作,降低了锁管理的开销,提升了数据库的整体性能。

  5. 灵活的事务处理:支持不同的隔离级别,可以根据需求灵活配置,以平衡性能和一致性。

2. MVCC实现原理

MVCC 的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 隐式字段undo日志 Read View 来实现的。所以我们先来看看它们的概念

隐藏字段

在内部,InnoDB 存储引擎为每行数据添加了三个 隐藏字段:

  • DB_TRX_ID(6字节):表示最后一次插入或更新该行的事务 id。此外,delete 操作在内部被视为更新,只不过会在记录头 Record header 中的 deleted_flag 字段将其标记为已删除
  • DB_ROLL_PTR(7字节) 回滚指针,指向该行的 undo log 。如果该行未被更新,则为空
  • DB_ROW_ID(6字节):如果没有设置主键且该表没有唯一非空索引时,InnoDB 会使用该 id 来生成聚簇索引

Read View

Read View 主要是用来做可见性判断,里面保存了 “当前对本事务不可见的其他活跃事务”:

主要有以下字段:

  • creator_trx_id:创建这个Read View的事务ID。这个ID标识了创建Read View的事务,而不是记录中的trx_id。如果是只读事务,事务ID默认为0。
  • m_ids:表示创建Read View时当前系统中活跃的读写事务的事务ID列表。这些是还未提交的事务,即启动了但还没提交的事务。

  • m_up_limit_id:表示创建Read View时活跃的事务中最小的事务ID。

  • m_low_limit_id:表示创建Read View时系统中应该分配给下一个事务的ID值,也就是当前最大事务ID+1。

class ReadView {
  /* ... */
private:
  trx_id_t m_low_limit_id;      /* 大于等于这个 ID 的事务均不可见 */

  trx_id_t m_up_limit_id;       /* 小于这个 ID 的事务均可见 */

  trx_id_t m_creator_trx_id;    /* 创建该 Read View 的事务ID */

  trx_id_t m_low_limit_no;      /* 事务 Number, 小于该 Number 的 Undo Logs 均可以被 Purge */

  ids_t m_ids;                  /* 创建 Read View 时的活跃事务列表 */

  m_closed;                     /* 标记 Read View 是否 close */
}

Read View 的作用主要是用来做可见性判断。当一个事务执行快照读操作时,它会使用Read View来判断自己能够看到哪个版本的数据。具体来说,系统会根据以下规则来判断记录的可见性:

不同的事务隔离级别下,Read View的生成时机也不同。例如,在READ COMMITTED隔离级别下,每次执行SELECT操作时都会生成一个新的Read View;而在REPEATABLE READ隔离级别下,只有在事务的第一次SELECT操作时会生成一个Read View,之后的查询都会复用这个Read View

  • 如果记录的trx_id等于creator_trx_id,说明这条记录是由当前事务自己修改的,因此对该事务是可见的。
  • 如果记录的trx_id小于m_up_limit_id,说明这个版本的记录是在创建Read View之前已经提交的事务生成的,所以该版本的记录对当前事务是可见的。
  • 如果记录的trx_id大于或等于m_low_limit_id,说明这个版本的记录是在创建Read View之后才启动的事务生成的,所以该版本的记录对当前事务是不可见的。
  • 如果记录的trx_id在m_up_limit_id和m_low_limit_id之间,就需要检查trx_id是否在m_ids列表中。如果在列表中,表示这个事务在Read View生成时刻还在活跃,还没有提交,因此该版本的记录对当前事务是不可见的;如果不在列表中,则说明这个事务在Read View生成之前就已经提交了,因此该版本的记录对当前事务是可见的。

事务可见性示意图


undo-log

undo log 主要有两个作用:

  • 当事务回滚时用于将数据恢复到修改前的样子
  • 另一个作用是 MVCC ,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现非锁定读(快照读)

InnoDB 存储引擎中 undo log 分为两种:insert undo logupdate undo log

  1. insert undo log:指在 insert 操作中产生的 undo log。因为 insert 操作的记录只对事务本身可见,对其他事务不可见,故该 undo log 可以在事务提交后直接删除。不需要进行 purge 操作。insert 时的数据初始状态:
  2. update undo logupdatedelete 操作中产生的 undo log。该 undo log可能需要提供 MVCC 机制,因此不能在事务提交时就进行删除。提交时放入 undo log 链表,等待 purge线程 进行最后的删除

数据第一次被修改时:

数据第二次被修改时:

不同事务或者相同事务的对同一记录行的修改,会使该记录行的 undo log 成为一条链表,链首就是最新的记录,链尾就是最早的旧记录。


数据可见性算法

InnoDB 存储引擎中,创建一个新事务后,执行每个 select 语句前,都会创建一个快照(Read View),快照中保存了当前数据库系统中正处于活跃(没有 commit)的事务的 ID 号。其实简单的说保存的是系统中当前不应该被本事务看到的其他事务 ID 列表(即 m_ids)。当用户在这个事务中要读取某个记录行的时候,InnoDB 会将该记录行的 DB_TRX_IDRead View 中的一些变量及当前事务 ID 进行比较,判断是否满足可见性条件。

1. 如果记录 DB_TRX_ID<m_up_limit_id,那么表明最新修改该行的事务(DB_TRX ID)在当前事务创建快照之前就提交了,所以该记录行的值对当前事务是可见的
2. 如果 DB TRX ID >= m_low_limit_id,那么表明最新修改该行的事务(DB_TRX_ID)在当前事务创建2快照之后才修改该行,所以该记录行的值对当前事务不可见。跳到步骤5
3.m_ids 为空,则表明在当前事务创建快照之前,修改该行的事务就已经提交了,所以该记录行的值对当前事务是可见的
4. 如果 m_up_limit id <= DB TRX ID<m_low_limit_id,表明最新修改该行的事务(DB TRX ID)在4.当前事务创建快照的时候可能处于“活动态”或者“已提交状态”;所以就要对活跃事务列表 m iàs 进行查找(源码中是用的二分查找,因为是有序的)

  • 如果在活跃事务列表 m ids 中能找到 DB TRX ID,表明:① 在当前事务创建快照前,该记录行的值被事务 ID 为 DB TRX ID 的事务修改了,但没有提交;或者 ② 在当前事务创建快照后,该记录行的值被事务 ID 为 DB TRX ID 的事务修改了。这些情况下,这个记录行的值对当前事务都是不可见的。跳到步骤 5
  • 在活跃事务列表中找不到,则表明“id 为 trx_id 的事务”在修改“该记录行的值”后,在“当前事务”创建快照前就已经提交了,所以记录行对当前事务可见

5. 在该记录行的 DB_ROLL_PTR 指针所指向的 undo 1og取出快照记录,用快照记录的DB_TRX_ID 跳到步骤1重新开始判断,直到找到满足的快照版本或返回空。

3. RC和RR隔离级别下MVCC的差异

在MySQL的InnoDB存储引擎中,MVCC(多版本并发控制)是实现事务隔离级别的重要机制。MVCC通过保存数据的多个版本来允许读操作和写操作并发执行,从而提高数据库的并发性能。在RC(Read Committed)和RR(Repeatable Read)这两种隔离级别下,MVCC的工作方式有所不同。

RC隔离级别下的MVCC:

  • 在RC隔离级别下,每次执行快照读(SELECT)操作时,都会生成一个新的Read View。这意味着每次SELECT操作都可能看到不同的数据行版本,因为它们是基于各自时刻的Read View来判断数据的可见性。
  • Read View中包含了当前活跃的事务ID列表,以及这些事务的最小和最大事务ID。
  • 当读取一行数据时,会根据Read View来判断该数据行的版本是否对当前事务可见。

RR隔离级别下的MVCC:

  • 在RR隔离级别下,只有在事务的第一个快照读操作时会生成一个Read View,之后的快照读操作都会复用这个Read View。这样,同一个事务中多次执行SELECT操作,看到的都是事务开始时的数据行版本,从而保证了可重复读。
  • 这就意味着在RR隔离级别下,一个事务看不到其他事务在其第一个快照读之后对数据所做的修改。

4. MVCC+Next-key-Lock 防止幻读

InnoDB 存储引擎在 RR级别下通过 vcc和 Next-key Lock 来解决幻读问题。(在RC隔离级别下,由于每次SELECT操作都可能读取到最新的数据版本,因此不能避免幻读。)

1. 执行普通 select,此时会以 MVCC 快照读的方式读取数据

在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成 Read View ,并使用至事务提交。所以在生成 Read View 之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”

2、执行 select...for update/lock in share mode、insert、update、delete 等当前读

在当前读下,读取的都是最新的数据,如果其它事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读!InnoDB 使用 Next-key Lock 来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读

这种锁定机制(Next-Key Lock)有效地解决了幻读问题,幻读是指事务在前后两次查询同一个范围的时候,第二次查询看到了第一次查询没有看到的行。由于Next-Key Lock的存在,当事务A在读取某个范围内的记录时,事务B想要在这个范围内插入一条新的记录将会被阻塞,直到事务A释放了锁。这样,事务A再次查询时,就不会看到那些原本不存在的“幻读”行.

参考


  • 《MySQL 技术内幕 InnoDB 存储引擎第 2 版》
  • Innodb 中的事务隔离级别和锁的关系
  • MySQL 事务与 MVCC 如何实现的隔离级别
  • InnoDB 事务分析-MVCC
  • InnoDB存储引擎对MVCC的实现 | JavaGuide

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2168956.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通信工程学习:什么是FDD频分双工

FDD:频分双工 FDD(频分双工,Frequency Division Duplexing)是一种无线通信技术,它通过将频谱划分为上行和下行两个不重叠的频段来实现同时双向通信。以下是FDD频分双工的详细解释: 一、定义与原理 定义: FDD是一种无线通信系统的工作模式,其中上行链路(从移动…

以Flask为基础的虾皮Shopee“曲线滑块验证码”识别系统部署

以Flask为基础的虾皮Shopee“曲线滑块验证码”识别系统部署 一、验证码类型二、简介三、Flask应用 一、验证码类型 验证码类型&#xff1a;此类验证码存在两个难点&#xff0c;一是有右侧有两个凹槽&#xff0c;二是滑块的运动轨迹不是直线的&#xff0c;而是沿着曲线走的&…

您的业​​务端点是否完全安全?

根据 2023 年数据泄露调查报告&#xff0c;52% 的数据泄露涉及凭证泄露。这令人担忧&#xff0c;不是吗&#xff1f; 在当今的数字世界中&#xff0c;企业严重依赖技术&#xff0c;保护您的设备&#xff08;端点&#xff09;至关重要。这些设备&#xff08;包括计算机、笔记本…

MySQL从入门到精通 - 基础篇

一、MySQL概述 1. 数据库相关概念 二、SQL &#xff08;1&#xff09;SQL通用语法 &#xff08;2&#xff09;SQL分类 &#xff08;3&#xff09;数据定义语言DDL 数据库操作 表操作 数据类型 1. 数值类型 2. 字符串类型 二进制数据&#xff1a;以二进制格式&#xff08;0和…

uniapp 知识点

自定义导航 在page.json navigationstyle":"custom"navigateTo传参 页面传参只能onLoad(option)里面拿 px和upx的关系 在750设计图中&#xff0c;1px1upx 路由 navigateBack返回上一页 重定向 其实就是把当前页面干掉了 公共组件和页面共同点 computed,watc…

基于微信小程序的智能汽车充电站系设计与实现(源码+定制+文档)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Spring Boot技术:构建高效网上购物平台

第3章 系统分析 3.1 可行性分析 在系统开发之初要进行系统可行分析&#xff0c;这样做的目的就是使用最小成本解决最大问题&#xff0c;一旦程序开发满足用户需要&#xff0c;带来的好处也是很多的。下面我们将从技术上、操作上、经济上等方面来考虑这个系统到底值不值得开发。…

【Vue】Vue3 的初始化过程

核心流程是patch&#xff0c;然后Patch有一个分支&#xff0c;分别处理组件和浏览器原生标签。分别对应processElement和processComponent&#xff0c;从上到下插入&#xff0c;知道处理完成&#xff0c;才把顶层div插入到浏览器。“一次性渲染&#xff0c;而不是一个个一个渲染…

[论文笔记] Chain-of-Thought Reasoning without Prompting

分析: 在CoT解码路径中,我们可以看到模型在第三个位置(𝑖? = 3)开始展示推理过程,并且给出了正确的答案“8”。模型首先识别出说话者有3个苹果,然后识别出爸爸比说话者多2个,即5个苹果,最后将这两个数量相加得到总数8个苹果。 这个例子表明,通过探索替代的解码路径…

【每天学个新注解】Day 7 Lombok注解简解(六)—@With

With 创建一个新的对象&#xff0c;该对象是当前对象的副本&#xff0c;但某些字段的值可以被更改。 1、如何使用 With 可以使用在类上&#xff0c;也可以使用在成员变量上。加在类上相当于给所有成员变量 With可以配合AccessLevel使用&#xff0c;创建出指定访问修饰符的wi…

多模态大模型学习(一)

参考&#xff1a;https://www.bilibili.com/video/BV1kT411o7a6?p2&spm_id_frompageDriver&vd_source156234c72054035c149dcb072202e6be 余弦相似度&#xff0c;让正样本内积趋近于1&#xff0c;负样本趋近于-1。度量学习。N特别大时&#xff0c;负样本远大于正样本&…

PHP之 实现https ssl证书到期提醒,通过企微发送消息

参考文章 https://blog.51cto.com/17099933344/1935194 https://blog.csdn.net/m0_37346206/article/details/127333463 https://www.cnblogs.com/tk-bolg/p/18108106 使用的企微接口 https://qyapi.weixin.qq.com/cgi-bin/message/send 查询 ssl证书到期时间 // ssl证书即将…

基于BiLSTM+Transformer混合模型实现交通流量时序预测(PyTorch版)

前言 系列专栏:【深度学习&#xff1a;算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域&#xff0c;讨论了各种复杂的深度神经网络思想&#xff0c;如卷积神经网络、循环神经网络、生成对…

2024年研究生数学建模“华为杯”E题——肘部法则、k-means聚类、目标检测(python)、ARIMA、逻辑回归、混淆矩阵(附:目标检测代码)

文章目录 一、情况介绍二、思路情况二、代码展示三、感受 一、情况介绍 前几天也是参加了研究生数学建模竞赛&#xff08;也就是华为杯&#xff09;&#xff0c;也是和本校的两个数学学院的朋友在网上组的队伍。昨天&#xff08;9.25&#xff09;通宵干完论文&#xff08;一条…

Windows安装openssl开发库

1 下载openssl安装包并安装 下载网址&#xff1a; https://slproweb.com/products/Win32OpenSSL.html 下载对应的安装版本。 双击安装包&#xff0c;一路下一步完成安装。注意&#xff1a;1.安装路径不要有空格&#xff1b; 2. 建议不要把DLL拷贝到系统路径。 2 编辑代码 …

“类型名称”在Go语言规范中的演变

Go语言规范&#xff08;The Go Programming Language Specification&#xff09;[1]是Go语言的核心文档&#xff0c;定义了该语言的语法、类型系统和运行时行为。Go语言规范的存在使得开发者在实现Go编译器时可以依赖一致的标准&#xff0c;它确保了语言的稳定性和一致性&#…

制造企业为何需要PLM系统?PLM系统解决方案对制造业重要性分析

制造企业为何需要PLM系统&#xff1f;PLM系统解决方案对制造业重要性分析 新华社9月23日消息&#xff0c;据全国组织机构统一社会信用代码数据服务中心统计&#xff0c;我国制造业企业总量突破600万家。数据显示&#xff0c;2024年1至8月&#xff0c;我国制造业企业数量呈现稳…

数据结构之链表(1),单链表

目录 前言 一、什么是链表 二、链表的分类 三、单链表 四、单链表的实现 五、SList.c文件完整代码 六、使用演示 总结 前言 本文讲述了什么是链表&#xff0c;以及实现了完整的单链表。 ❤️感谢支持&#xff0c;点赞关注不迷路❤️ 一、什么是链表 1.概念 概念&#xff1a;链…

【学习笔记】手写 Tomcat 六

目录 一、线程池 1. 构建线程池的类 2. 创建任务 3. 执行任务 测试 二、URL编码 解决方案 测试 三、如何接收客户端发送的全部信息 解决方案 测试 四、作业 1. 了解工厂模式 2. 了解反射技术 一、线程池 昨天使用了数据库连接池&#xff0c;我们了解了连接池的优…

在 Docker 版 RStudio 中安装 Seurat V4 的完整教程 (同样适用于普通R环境安装)

在单细胞RNA测序&#xff08;scRNA-seq&#xff09;数据分析领域&#xff0c;Seurat 是一个广泛使用且功能强大的R包&#xff0c;提供了丰富的数据处理和可视化工具。为了简化环境配置和依赖管理&#xff0c;使用Docker来部署RStudio并安装Seurat V4是一种高效且可重复的方法。…