MySQL深入理解MVCC机制(详解)

深入理解MVCC

1、MVCC定义

MVCC:Multi-Version Concurrency Control,多版本并发控制机制。

在mysql中,为了满足事务的四大特性之一的隔离性,就是当前事务中的查询的数据不受其他事务的增删改操作的影响,因此mysql主要是通过这个可串行化的这种隔离级别和现在要谈的mvcc机制来实现。而可串行化就是将所有的操作由并行改为串行,就是在每个增删改查操作上面都加了锁,因此性能非常低,因此mysql也并没有选择这个可串行化来作为mysql默认的隔离级别,而是使用可重复读。接下来就是主要谈一下这个可重复读事务中的mvcc的机制和底层原理。

2、undolog日志

在讲mvcc机制之前,首先要了解一下这个undolog日志。在mysql中,如果使用的是默认的可重复读的这个隔离级别,在一条更新语句中如果加了事务,那么在这个事务启动之后,提交之前,那么这条数据是暂时不会添加到数据库的,直到事务提交成功才会更新或者添加到数据库中。那么中间就需要实现数据的暂存,那么这种存储方式就是通过这个undolog日志实现的。

CREATE TABLE `product` (
  `id` bigint(20) NOT NULL,
  `product_id` int(11) DEFAULT NULL COMMENT '商品id',
  `version` int(11) DEFAULT NULL COMMENT '版本',
  `stock` int(11) DEFAULT NULL COMMENT '商品数量',
  `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  `is_deleted` tinyint(4) DEFAULT NULL COMMENT '是否删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

如上,创建一张表,新增一条数据

insert into stock values (1,1,0,100,now(),now(),0);

拿一条更新语句来说,如上面一张商品表,接下来要扣减一件商品的库存,一开始这件商品库存有100,现在扣减20,如果扣减成功,那么数据库的值就是80。

update product set stock = stock - 20 where id = 1;

如果在扣减库存时,发生异常数据回滚,那么此时数据需要回滚到之前的值,就需要一个日志来记录扣减之前的值,那么就是通过这个undolog来记录的。就是说在这个更新语句中,在开启事务之后,提交事务之前,这个库存100就会记录到undolog日志里面,减完后的80这个值如果整个事务没有发生异常那么就直接加入到数据库中,如果出现异常那么就通过undolog里面的值作为回滚的数据。一句话说这个undolog日志就是用来记录被修改的值,防止出现异常回滚的

3、undolog版本控制链

当然这个undolog日志也不是只记录一条,如在一个或者多个事务对这个库存进行多次修改,那么这个undolog就会形成一条历史版本控制链。在这个版本控制链中,有一个隐藏的事务id和指针。事务id在新增或者更新都会生成一个事务id,默认自增,指针所指向的就是当前数据修改前的一个历史数据,如果出现了回滚,那么就会根据这个链路依次往前回滚,直到找到上一个或者前面几个。

在这里插入图片描述

这个事务id在sql更新或者新增时生成,并非事务提交生成,因此可能出现事务大的id先提交,那么版本链路里面的事务id的大小就是乱序的。

4、readView
1、readView简介

在上面的undolog日志里面,可以发现确实记录了所有修改的值,也知道undolog是用来回滚的,但是会存在一个问题,如果单纯使用undolog来解决这个回滚问题,那么就会不知道回滚到链路中的哪一个节点,因此就需要通过readView来和这个undolog结合使用,通过readView来知道具体回滚到链路中的哪个节点。

在一个事务里面,执行任何查询都会生成当前事务的一致性视图read-view,在可重复读中的事务隔离级别中,该视图在事务结束之前都不会发生变化,当前如果是读已提交的隔离级别那么在每次执行sql时都会重新生成视图。

在可重复读的事务里面,这个readView视图由未提交的事务id数组和已创建的最大事务id(max_id)组成,因此这个最大的max_id可能在数组里面,也可能不在,因此事务最大的id可能先提交,而数组里面的id都是未提交的。

在这里插入图片描述

如上图,由四个事务ABCD,同时开启事务,同时去操作这个商品表的库存,事务ABC在执行更新语句之后,就会产生一个事务id,因为事务id都是自增的,因此从左往右事务依次递增。而事务D主要用来查询,并无增删改操作,主要是查询当前事务中的库存数量,由于没有更新和新增语句,因此也没有事务id。由于三个事务是同时开始,因此commit提交时间取决于更新语句的时间,谁先更新完谁先提交,因此可能会出现事务大的id先提交。

上图只要是针对库存表id为1的那一行数据进行操作的,而id=2只是为了通过更新语句给这个事务生成一个事务id

并且mvcc机制主要是针对于可重复读的这个隔离级别,因此在D中暂时只考虑查询有其他事务提交的数据,未提交之前的数据暂时不做select查询考虑。

2、readView和undolog结合使用规则

在使用readview只能看到当前的几个事务,并且不能得知事务的提交顺序,因此需要结合undolog一起使用,在两者结合使用之前,需要有一个readview视图和undolog版本的对比规则,接下来详细说明一下这个比对规则的一些命名:**假设这个数组中未提交的事务id数组的最小值假设为min_id ,已创建事务的事务id的最大id为max_id,undolog版本控制链的头节点为head节点。**如在下面的第三个select查询中,min_id = 102,max_id=104。

然后以这个min_id和这个max_id为分界线,小于min_id为已提交的事务,在这个两个值之间的为未提交或者已提交的事务,大于这个max_id的,为未开始的事务。

在这里插入图片描述

那么规则如下:

  • head节点的事务id<=min_id:已提交的事务,该事务可见
  • min_id<head节点的事务id<=max_id:未提交或者已提交的事务
  • max_id<head节点的事务id:未开始的事务,不可见
  • 如果事务id在数组中,表示事务未提交,不可见
  • 如果事务id不在数组中:表示事务已提交,不可见

总结:只要满足一个事务是可见的,那么这个版本控制链路对应结点的值就是需要找的值

3、readview和undolog基本使用

1,假设库存一开始为200,由于事务id是自增,那么可以暂时假设这个事务trx_id=101的值对应的库存就是200,那么在事务D中,在第一次查询之后就会生成一个readview视图,并且在事务提交之前,这个视图的值不会改变。接下来主要研究一下在这个RR的默认级别事务中,为何select查询的值可以不变化,以及readview和undolog匹配的过程是咋样的。
在这里插入图片描述

2,接下来看第一个select查询,此时事务ABC都因为有了更新语句,因此此时abc都有对应的事务,并且事务A已经提交事务,此时的事务readview组成如下,而undolog链路中的值如下图,因为主要是针对表中id为1的库存对应的版本链路,因此暂时只有两个数据,bc中两个更新语句只为了生成事务id,数据并不在一个undolog版本链路上,并且此时头节点head对应的事务id为102。

那么通过这个头结点head对应的事务和readview的视图进行对比,此时的head事务id为102,min_id为数组中最小值103,max_id为已创建的最大值id104,根据版本比对规则,符合第一条head结点的事务id小于min_id,即当前结点时可见的,只要获取到的值是可见的,那么查询到的值就是这个事务对应的值,即100。

//第一个select查询语句的值,由未提交的事务id数组和已创建的最大的事务id组成
[103,104],104

在这里插入图片描述

3,接下来看第二个select查询语句,此时的事务B提交了,事务B是操作id为1的商品数据,因此在更新时会将原始值加入到这个undolog的日志版本链路上。由于事务D并没有提交,因此此时的readview如下,和之前一样,但是undolog日志链路会多一条数据,其链路如下图

那么此时的头节点的值为事务id103,即head对应的事务id为103,min_id为103,max_id为104。根据版本对比规则,符合第二条,但是此时的head对应的事务id还在数组中,因此这个结点的数据并不可见。那么将继续对比下一个结点,下一个结点的事务id为102,符合第一条head结点的事务id小于min_id,即事务id为102对应的结点时可见的,那么查询到的值仍然时100

//第二个select查询语句的值,由未提交的事务id数组和已创建的最大的事务id组成
[103,104],104

在这里插入图片描述

4,接下来再看第三个select语句,第三个select查询语句就是在事务C提交之后进行查询的,那么此时的readview视图如下,依旧不变,因为操作的是id为1的值,因此undolog版本链路上会多一条数据。

此时的头节点head的事务id为104,min_id为103,max_id为104。根据版本对比规则,符合第二条,但是此时的head对应的事务id还在数组中,因此这个结点的数据并不可见;那么将继续对比下一个结点,下一个结点的事务id为103,符合第二条,但是此时的head对应的事务id还在数组中,因此这个结点的数据也不可见;接下来对比第三条,head结点的事务id为102,小于min_id103,即事务id为102对应的结点时可见的,那么查询到的值仍然时100

//第三个select查询语句的值,由未提交的事务id数组和已创建的最大的事务id组成
[103,104],104

在这里插入图片描述

因此不管后面有再多的其他事务更改,只要当前事务没有提交,那么当前事务对应的readview就不会改变,通过undolog的日志版本链路,并且结合readview的版本比对规则,就可以找到一个可见的事务对应的数据,那并且这个值一定是最先获取的值,就如上面商品的库存,即使数据库中的值真的变了,也可以通过这个mvcc机制来保证事务的隔离性,从而解决使用读写锁效率低慢的问题。一句话总结就是:根据数据版本链对比规则,来读取同一条数据在版本链上的不同版本数据,并且可以存在多个事务形成多个readview,但是版本链undolog只有一条

5、总结

mvcc被称为多版本并发控制机制,由于mysql中的事务默认的事可重复读,在这个隔离级别中并没有解决幻读问题,以此可以通过mvcc机制解决,并且还可以解决并发中读写锁,读写冲突问题,从而提高并发读写的性能和效率。mcvv机制主要是通过undolog的日志版本控制链路和readview视图组成。undolog链路中的每个节点有一个事务id和一个指针组成,事务id是在更新或者插入数据时生成,指针是用来指向上一个版本,在执行完更新语句时就会将这个事务id加入到版本链路中;readview视图由未提交的事务id数组和已创建和最大事务id组成,并且在一个事务中,第一次select查询就会生成一个readview视图,并且在该事务提交之前该事务的readview视图不会改变,然后根据readview视图比对规则,其规则就是将undolog链路中的头节点为head节点,将数组中的最小id为min_id,将已创建的id为最大max_id,然后根据视图对比规则,找到一个事务id是可见的,那么找到的第一个可见的值,该事务id对应的节点的值就是需要查询的值。主要是通过版本链对比规则,来读取同一条数据版本链路上不同的数据。这样就可以保证在一个事务中查询的值可以一直不变,不受其他事务的影响,并且这种方案的效率远远高于读写锁。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

怎么解决端口被占用

目录 一、引言 二、解决方法 一、引言 最近用vscode写网页&#xff0c;老是遇见端口被占用&#xff0c;报错如下&#xff1a; listen tcp :8080: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted. 二、解决方法 1.换…

Apache DolphinScheduler 3.3.0 版本重磅更新提前看!

Apache DolphinScheduler 3.3.0版本终于要在万众期待中发布啦&#xff01;本次发版将有重大功能更新&#xff0c;包括架构上的调整。 为了让广大用户提前尝鲜&#xff0c;社区特别准备了直播活动提前揭秘3.3.0版本中的重要更新&#xff0c;到时候你将会了解到这些信息&#xf…

苹果平板HOME键成历史,全面屏时代到来?2024平板电脑市场趋势分析

近期苹果公司在“放飞吧”发布会上推出了新款iPad Pro和iPad Air平板电脑&#xff0c;并下架了最后一款带有实体Home按键的iPad 9。这一变化标志着Home键在苹果iPad产品线中成为了历史&#xff0c;引起了不少网友的怀念和感慨。 与此同时&#xff0c;今年3月线上平板电脑市场迎…

分析:Palo Alto在从SASE向SASO演进中定位不佳

摘要 我们通过上一篇文章&#xff08;Fortinet的愿景——超越SASE&#xff09;中应用于Fortinet的相同框架来回顾Palo Alto Network在网络和网络安全方面的前景。 SASE涉及数据传输的第一英里。不过&#xff0c;随着SASE的发展&#xff0c;投资者还需要考虑中间和最后一英里。…

QLabel 如何同时显示图片和文字?

效果: align="top"表示图片和文字底部对齐。 img src=":/img/qrc_img.png"表示此图片被添加到qrc的相对路径。 完整: QString content =QString("<html><head/><body><p><img src=\":/img/qrc_img.png\"…

【linux kernel】linux内核hid触摸源码hid-multitouch.c剖析

文章目录 一、内核中通用hid触摸驱动二、probe过程剖析(1)hid_parse()函数(2)hid_hw_start()函数(3)hid_connect()函数三、hid-multitouch.c应用场景一、内核中通用hid触摸驱动 在linux内核中,为HID触摸面板实现了一个通用的驱动程序,位于/drivers/hid/hid-multitouch.c文件…

【高阶数据结构】图--邻接矩阵、邻接表、BFS、DFS、Kruskal、Prime

图--邻接矩阵、邻接表、BFS、DFS、Kruskal、Prime 一、图的概述1、概述&#xff08;纯理论部分&#xff09;2、邻接矩阵&#xff08;实现一个添加边的图&#xff09;&#xff08;1&#xff09;思路介绍&#xff08;2&#xff09;代码部分&#xff08;3&#xff09;测试部分 3、…

pytest教程-40-钩子函数-pytest_runtest_call

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest_runtest_setup钩子函数的使用方法&#xff0c;本小节我们讲解一下pytest_runtest_call钩子函数的使用方法。 pytest_runtest_call 钩子函数在 pytest 调用测试函数&#xff08;即测试用…

JAVA栈相关习题3

1.将递归转化为循环 比如&#xff1a;逆序打印链表 // 递归方式void printList(Node head){if(null ! head){printList(head.next);System.out.print(head.val " ");}} // 循环方式void printList(Node head){if(nullhead){return;}Stack<Node> snew Stack<…

将大概的流程具体还是看源码

之前看源码的时候呢没有文字整理&#xff0c;想来还是写一个大概的流程吧&#xff0c;具体是无法用文字描述 spring源码真的yyds&#xff0c;数据结构 反射 父子类 接口…玩得溜到飞起 博大精深呐 后期不断喜欢ing&#xff01; springApplication.run方法 获取了一个Configu…

STC8增强型单片机开发——库函数

一、使用库函数点灯 导入库函数。 下载STC8H的库函数&#xff1a;&#x1f4ce;STC8G-STC8H-LIB-DEMO-CODE_2023.07.17_优化版.zip 来到库函数的目录下&#xff0c;拷贝以下文件&#xff1a; Config.hType_def.hGPIO.hGPIO.c 新建项目&#xff0c;将拷贝的4个文件放到项目目录…

【管理咨询宝藏96】企业数字化转型的中台战略培训方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏96】企业数字化转型的中台战略培训方案 【格式】PDF版本 【关键词】SRM采购、制造型企业转型、数字化转型 【核心观点】 - 数字化转型是指&…

代码审计-php篇之某CRM系统多处sql注入

&#x1f31f; ❤️ 作者&#xff1a;yueji0j1anke 首发于公号&#xff1a;剑客古月的安全屋 字数&#xff1a;3516 阅读时间: 35min 声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果…

科沃斯,「扫地茅」荣光恐难再现

作者 | 辰纹 来源 | 洞见新研社 科沃斯恐怕已经很难再回到被市场誉为“扫地茅”时的荣光了。 不久前&#xff0c;科沃斯发布2023年财报&#xff0c;报告期内营业收入155亿&#xff0c;同比仅增长1.16%&#xff0c;归母净利润6.12亿元&#xff0c;同比下降63.96%&#xff0c;直…

【北京迅为】《iTOP-3588开发板快速烧写手册》-第11章 救砖方法

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

pycharm中导入rospy(ModuleNotFoundError: No module named ‘rospy‘)

1. ubuntu安装对应版本ros ubuntu20.04可参考&#xff1a; https://wiki.ros.org/cn/noetic/Installation/Ubuntuhttps://zhuanlan.zhihu.com/p/515361781 2. 安装python3-roslib sudo apt-get install python3-roslib3.在conda环境中安装rospy pip install rospkg pip in…

重定向_缓冲区

目录 重定向 文件属性操作 浅谈重定向​编辑 深入重定向 dup2 缓冲区 缓冲区的理论理解 代码分析 重定向 文件属性操作 #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *path, struct stat *buf); int fstat(i…

USB系列一:USB技术概念

在这里USB的历史就不赘述了&#xff0c;有兴趣可以自己去搜索。也省略掉USB接口的概述&#xff0c;这些都是一些飞技术性的常识性的知识&#xff0c;没必要浪费篇幅和文字来描述。 一、USB总线版本&#xff1a;&#xff08;从USB1.1说起&#xff09; 1、USB1.1 1998年9月23日…

教你如何高效使用Java中的ArrayList

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

【vue+el-upload】当action=“#“,代表不使用默认上传,使用自定义上传,http-request获取文件流

el-upload有多种上传行为&#xff1a; 1、立即上传&#xff1a; 当 action 属性被赋予一个有效的 URL 时&#xff0c;一旦用户选择了文件&#xff0c;el-upload 组件会立即自动将文件上传到指定的服务器地址。 2、不立即上传&#xff08;自定义触发&#xff09;&#xff1a; 如…
最新文章