MySQL MVCC的详解之Read View

news2024/10/6 20:25:08

文章目录

    • 概要
    • 一、基于UNDO LOG的版本链
        • 1.1、行记录结构
        • 1.2、了解UNDO LOG
        • 1.3、版本链
    • 二、Read View
        • 2.1、判定机制
    • 三、参考

概要

在上文中,我们提到了MVCC(Multi-Version Concurrency Control)多版本并发控制,是通过undo log来实现的。那具体是如何实现的呢?将在本文一一道来。

MVCC是为了实现非阻塞读,即提高数据库并发读能力的一种机制。

通常来说,A事务正在修改数据行X,在修改未结束前,B事务要读数据行X,为了避免读到脏数据,B就会被阻塞,直到A事务修改完数据行X,MVCC很好的避免了这种情况的发生。

MVCC是通过保存数据在某个时间点的快照来实现的,即保存一个数据行的多个变更版本(空间换时间)。这些版本就是undo log了,每一行的变更记录就存在undo log中,通过链表联系在一起,构成了一个完整的版本链,供MVCC实现非阻塞读。

例如在可重复读隔离级别下,A事务正在修改数据行X,在修改数据行X前会把其当前记录插入到版本链中,B事务要读数据行X就到版本链中找符合的,这样B就不会被阻塞了。

ps:MySQL的MVCC只作用于在REPEATABLE READ和READ COMMITED两个隔离级别下执行普通的SELECT操作。

在高性能MySQL第三版一书中对MVCC的操作描述如下:
高性能MySQL第三版一书中对MVCC的操作描述下面来一起探索下具体实现吧。

本文背景:MySQL InnoDB存储引擎。

一、基于UNDO LOG的版本链

在了解版本链之前,首先看一下InnoDB存储引擎的行记录。

1.1、行记录结构

提到MySQL的行记录,肯定会想到行ID、用户数据列等内容,除了这些信息外,还有一些隐藏信息,比如事务ID、回滚指针等其他额外信息,那我们可以得出下图:
MySQL行记录结构简单示意图
其中事务ID(trx_id)、回滚指针(rollback_ptr)是本文要讲的核心。

ps:InnoDB的行记录是存储在聚族索引中的

1.2、了解UNDO LOG

                                            MySQL undo log结构示意图

MySQL undo log结构示意图

MySQL的undo log分为两大类:

  1. insert undo:insert 操作产生的,记录了table_id、trx_id、主键各列数据等信息。
  2. update undo:update和delete操作产生的,

虽说update和delete操作产生的undo log都会记录到update undo这个大类,但其记录内容是有很大差距的。
delete操作产生的undo log会记录table_id、trx_id、rollback_ptr、主键各列数据等信息,而update操作产生的undo log会记录更新table_id、trx_id、rollback_ptr、被更新列旧值、主键各列数据等信息。

ps:可以看到undo log中并没有记录用户列数据

1.3、版本链

我们现在在test库下有一个test表:
MySQL 测试表下面我们经过一系列插入,删除,更新来演示版本链的变迁:
假设当前全局trx_id = 101。

  1. 插入一条数据
insert into test (id,num,name,key_id) values (1,1,"bob",11);

此时有:
插入一条数据

  1. 更新数据
    1)令id=1的数据name=tom
update test set name = "tom" where id = 1;

此时有:
更新一条数据
2)令id=1的数据name=join

update test set name = "join" where id = 1;

此时有:
更新一条数据

  1. 删除数据,删除id=1的数据
delete from test where id = 1;

此时有:
删除一条数据
如上图,在对id=1这一条记录的插入,更新,删除的过程中构建了一个版本链。其中删除操作,只是在聚簇索引上的记录中打了删除标记,并不会立即删除,而是当没有Read View持有该事务ID时才会有purge线程去真的去删除,之后这块空间才能被使用,为什么不能立即删除呢,主要是因为undo log中并不保存所有的用户列数据,甚至不保存,都是基于聚簇索引中的记录行,在结合undo log内容,在回滚过程中构造某个版本的数据。

另外,我们在1.2小节中强调了 undo log中并不记录用户列数据,这里只是为了表示方便才画了出来,其实MySQL是基于聚簇索引上的记录内容,通过从聚簇索引上的记录roll_ptr开始,依次回滚,直到遇到符合要求的事务ID,构造出最终数据。

假设第二次更新操作开始但未提交时,有个trx_id=107的事务要读id=1的数据,此时发现ID=1的数据trx_id=106且处于活跃状态,则需要沿着版本链回滚,当遇到trx_id=102的记录,结合聚簇索引上的记录和trx_id=102的undo记录,构造出trx_id=102的完整数据,对于trx_id=107的事务来说,该事务是已提交的事务,则读取即可。其实这就是Read View了,详情请看下一章节。

二、Read View

对于READ COMMITED隔离级别,需要读已经提交的数据,那当A事务修改完聚簇索引上的记录X后尚未提交,此时B事务读取记录X,按照定义,此时聚簇索引上的记录是不允许读取的,如何判定呢?就依赖Read View机制了

对于REPEATABLE READ隔离级别,需要重复读数据,那当A事务读取记录X后,B事务修改完聚簇索引上的记录X并提交,此时A事务需要在此读取记录X,按照定义,此时聚簇索引上的记录是不允许读取的,如何判定呢?也就依赖Read View机制了

Read View也称作一致性视图,其主要包含4个主要的内容:

  • m_ids:在生成Read View时,当前系统中活跃的读写事务的事务ID列表;
  • min_trx_id:在生成Read View时,当前系统中活跃的读写事务中最小的事务ID,即m_ids中的最小值;
  • max_trx_id:在生成Read View时,当前系统中活跃的读写事务中最大的事务ID,即系统应该分配给下一个事务的事务ID(全局事务ID的值);
  • creator_trx_id:在生成Read View时,当前事务的事务ID。

其中max_trx_id要注意下,并不一定是m_ids中的最大值,而是生成Read View时的全局事务ID值。因为事务ID时递增(循环)分配的,在RR隔离级别下,假设当前活跃的事务ID有1,2,3,事务ID=3的事务提交后,再开启一个事务A,发起读操作,此时Read View时m_ids=[1,2,4],min_trx_id=1,max_trx_id=4,如果有另一个写事务B提交了,消耗了一个事务ID=4,那么此时事务A进行写操作,就会出现creator_trx_id=5的情况。

为什么会这样呢?我们要明白事务ID的生成并不是开启事务(执行begin操作)时就确定的,而是第一次执行写操作时确定的
而Read View生成时机是在读操作前确定的,但RC与RR还不同,RC是每次读操作前都生成一个Read View(保证可以读已提交数据),而RR是在第一次读操作前生成一个Read View,就不会变动了(保证可重复读)

2.1、判定机制

MySQL根据Read View读要访问的记录依次进行以下判定,来决定是否可访问:

  1. 如果被访问记录的trx_id等于creator_trx_id相等,这意味当前事务在访问它自己修改的记录,允许被访问;
  2. 如果被访问记录的trx_id小于min_trx_id,这意味被访问记录在当前事务生成Read View时已经提交了,允许被访问;
  3. 如果被访问记录的trx_id大于等于max_trx_id,这意味被访问记录在当前事务生成Read View之后产生的,不允许被访问;
  4. 如果被访问记录的trx_id在m_ids中,说明在当前事务生成Read View时被访问记录所属的事务还是活跃的,不允许被访问;
  5. 如果被访问记录的trx_id不在m_ids中,说明在当前事务生成Read View时被访问记录所属的事务已经提交了,允许被访问;

以上的判定机制,是实现RC和RR的基础。

三、参考

1]:庖丁解InnoDB之Undo LOG
2]:正确的理解MySQL的MVCC及实现原理

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

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

相关文章

MATLAB | 七夕节用MATLAB画个玫瑰花束叭

Hey又是一年七夕节要到了,每年一次直男审美MATLAB绘图大赛开始hiahiahia,真的这些代码越写越不知道咋写,又不想每年把之前的代码翻出来再发一遍,于是今年又对我之前写的老代码进行了点优化组合,整了个花球变花束&#…

计算机网络-物理层(三)-信道的极限容量

计算机网络-物理层(三)-信道的极限容量 当信号在信道中传输失真不严重时,在信道的输出端,这些信号可以被识别 当信号在信道中,传输失真严重时,在信道的输出端就难以识别 造成失真的因素 码元传输速率信号传输距离噪声干扰传输媒…

8.4.tensorRT高级(3)封装系列-infer推理封装,输入输出tensor的关联

目录 前言1. infer封装总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程,之前有看过一遍,但是没有做笔记,很多东西也忘了。这次重新撸一遍,顺便记记笔记。 本次课程学习 tensorRT 高级-infer推理封装,输入输出…

思维进化算法(MEA)优化BP神经网络

随着计算机科学的发展,人们借助适者生存这一进化规则,将计算机科学和生物进化结合起来,逐渐发展形成一类启发式随机搜索算法,这类算法被称为进化算法(Evolutionary Com-putation, EC)。最著名的进化算法有:遗传算法、进化策略、进化规划。与传统算法相比,进化算法的特点是群体搜…

嵌入式设备应用开发(boost库应用)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 嵌入式开发过程中不可避免在很多情况下,需要使用到posix的api函数。一方面,这些api函数确实可以帮助我们解决一些问题;但是另外一方面,因为平台的差异,如果一段时间不做嵌入式…

Java集合利器 Map Set

Map & Set 一、概念二、Map三、Set下期预告 一、概念 Map和Set是一种专门用来进行搜索的数据结构,其搜索的效率与其具体的实例化子类有关。它们分别定义了两种不同的数据结构和特点: Map(映射) :Map是一种键值对&…

Spring高手之路13——BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor解析

文章目录 1. BeanFactoryPostProcessor 概览1.1 解读 BeanFactoryPostProcessor1.2. 如何使用 BeanFactoryPostProcessor 2. BeanDefinitionRegistryPostProcessor 深入探究2.1 解读 BeanDefinitionRegistryPostProcessor2.2 BeanDefinitionRegistryPostProcessor 的执行时机2.…

Redis+SpringBoot企业版集群实战------【华为云版】

目录 安装 复制及集群 bgsave rdb aof SpringBoot+Redis操作

斯特林近似(Stirling‘s approximation)

斯特林公式(Stirling’s approximation)是一条用来取n的阶乘的近似值的数学公式。一般来说,阶乘的计算复杂度为线性。当要为某些极大的n求阶乘时,常见的方法复杂度不可接受。斯特林公式能够将求解阶乘的复杂度降低到对数级。而且&…

在云服务器上安装Jenkins

说明:Jenkins是一个部署项目的平台,通过Jenkins可以省去从项目开发–>部署项目之间的所有流程,做到代码提交即上线。本文介绍在云服务CentOS上安装Jenkins。 前提 安装Jenkins之前,先要在云服务上安装JDK、Maven、Git&#x…

SpringAOP原理:手写动态代理实现

0、基础知识 AOP我们知道,是在不修改源代码的情况下,为代码添加一些新功能的技术。通过动态代理,可以在不修改原始类代码的前提下,对方法进行拦截和增强。 动态代理常用于在不改变原有业务逻辑的情况下,对方法…

Dockerfile部署LNMP

文章目录 一、项目模拟1. 项目环境2. 服务器环境3. 任务需求 二、Linux 系统基础镜像三、Nginx1. 建立工作目录2. 编写 Dockerfile 脚本3. 准备 nginx.conf 配置文件4. 生成镜像5. 创建自定义网络6. 启动镜像容器7. 验证 nginx 三、Mysql1. 建立工作目录2. 编写 Dockerfile3. 准…

CompletableFuture总结和实践

CompletableFuture被设计在Java中进行异步编程。异步编程意味着在主线程之外创建一个独立的线程,与主线程分隔开,并在上面运行一个非阻塞的任务,然后通知主线程进展,成功或者失败。 一、概述 1.CompletableFuture和Future的区别&…

Centos7查看磁盘和CUP统计信息iostat命令

Centos7查看磁盘和CUP统计信息iostat命令 Centos7内存高|查看占用内存命令 docker实战(一):centos7 yum安装docker docker实战(二):基础命令篇 docker实战(三):docker网络模式(超详细) docker实战(四):docker架构原理 docker实战(五):docker镜像及仓库配置 docker实战(六…

Interlij IDEA 运行 ruoyi 后端项目。错误: 找不到或无法加载主类 com.ruoyi.auth.RuoYiAuthApplication

错误: 找不到或无法加载主类 com.ruoyi.auth.RuoYiAuthApplication 用了 IDEA运行,参考以下issue删除.idea目录也没有用 (官方文档写是用Eclipse运行) 错误: 找不到或无法加载主类 com.ruoyi.auth.RuoYiAuthApplication Issue #I48N2X 若依/RuoYi-C…

Seaborn数据可视化(二)

目录 1.Seaborn风格设置 1.1 主题设置 1.2 轴线设置 1.3 移除轴线 1.4 使用字典传递函数 2.设置绘图元素比例 2.1 设置绘图元素比例paper 2.2 设置绘图元素比例poster 2.3 设置绘图元素比例notebook Seaborn将Matplotlib的参数划分为两个独立的组合,第一组用于…

关于Coursera网站视频无法观看

文章目录 前言找Ip 改hosts验证 前言 众所周知,coursera是很不错的学习网站,但由于国内访问限制,导致我的学习之路举步维艰 在科学上网彻底崩盘后,终于断了我的学习热情(真的很想骂人) 网站只能登入&#…

Qt6之如何为QDialog添加最大化和最小化按钮

在QDialog构造函数中添加以下几行代码: // 设置窗体最大化和最小化Qt::WindowFlags windowFlag Qt::Dialog;windowFlag | Qt::WindowMinimizeButtonHint;windowFlag | Qt::WindowMaximizeButtonHint;windowFlag …

No115.精选前端面试题,享受每天的挑战和学习

文章目录 变量提升和函数提升的顺序Event Loop封装 FetchAPI,要求超时报错的同时,取消执行的 promise(即不继续执行)强缓存和协商缓存的区别token可以放在cookie里吗? 变量提升和函数提升的顺序 在JavaScript中&#…

Cesium之水流模型

关于Primitive。 Primitive和Entity,一般翻译成图元和实体,图元更接近底层,实体是封装后的高级对象,使用更加简便。一般来说,Primitive的使用相对繁琐,相比Entity需要使用者自己初始化更多对象&#xff0c…