【MySQL进阶-05】深入理解mvcc机制(详解)

article/2025/9/15 14:07:23

MySql系列整体栏目


内容链接地址
【一】深入理解mysql索引本质https://blog.csdn.net/zhenghuishengq/article/details/121027025
【二】深入理解mysql索引优化以及explain关键字https://blog.csdn.net/zhenghuishengq/article/details/124552080
【三】深入理解mysql的索引分类,覆盖索引(失效),回表,MRRhttps://blog.csdn.net/zhenghuishengq/article/details/128273593
【四】深入理解mysql事务本质https://blog.csdn.net/zhenghuishengq/article/details/127753772
【五】深入理解mvcc机制https://blog.csdn.net/zhenghuishengq/article/details/127889365
【六】深入理解mysql的内核查询成本计算https://blog.csdn.net/zhenghuishengq/article/details/128820477
【七】深入理解mysql性能优化以及解决慢查询问题https://blog.csdn.net/zhenghuishengq/article/details/128854433
【八】深入理解innodb和buffer pool底层结构和原理https://blog.csdn.net/zhenghuishengq/article/details/128993871
【九】深入理解mysql执行的底层机制https://blog.csdn.net/zhenghuishengq/article/details/128100377
【十】深入理解mysql集群的高可用机制https://blog.csdn.net/zhenghuishengq/article/details/126239652
【彩蛋篇】深入理解顺序io和随机iohttps://blog.csdn.net/zhenghuishengq/article/details/129080088

深入理解mvcc机制

  • 一,MVCC定义
    • 1,undolog日志
    • 2,undolog版本控制链
    • 3,readView
      • 3.1,readview简介
      • 3.2,readview和undolog结合使用规则
      • 3.3,readview和undolog基本使用
    • 4,总结

一,MVCC定义

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

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

1,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来记录的。就是说在这个update更新语句中,在开启事务之后,提交事务之前,这个库存100就会记录在undolog日志里面,减完后的80这个值如果整个事务没有出现异常那么就直接加入到数据库里面,如果出现异常那么就将undolog里面的值作为回滚数据。一句话说这个undolog日志就是用来记录被修改的值,防止出现异常回滚的

2,undolog版本控制链

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

在这里插入图片描述

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

3,readView

3.1,readview简介

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

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

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

[trx_id1,trx_id2],max_id

在这里插入图片描述

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

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

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

3.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:未提交的事务或者已提交的事务

​ 如果事务id在数组中:表示事务未提交,不可见

​ 如果事务id不在数组中:那么表示已提交,是可见的

max_Id < head结点的事务id : 未开始的事务,不可见

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

3.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只有一条

4,总结

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


http://chatgpt.dhexx.cn/article/weLWPLHL.shtml

相关文章

MVCC及实现原理

&#xff01;首先声明&#xff0c;MySQL 的测试环境是 5.7 前提概要 什么是 MVCC什么是当前读和快照读&#xff1f;当前读&#xff0c;快照读和 MVCC 的关系MVCC 实现原理 隐式字段undo日志Read View整体流程MVCC 相关问题 RR 是如何在 RC 级的基础上解决不可重复读的&#xf…

4gl程式debug常用技巧

1、带参数的D&#xff1a; ‘MISC’表示具体料号传入 2、指定行数设置断点 比如我要在程式axmt360中的211行开始D 进入Debug界面后按ctrlD 弹出界面 输入b 行数 点OK 出现上面表示已设置OK&#xff0c;然后点cancel或叉叉退出 最后点下面按扭&#xff0c;程式跑到210行时自…

【OpenGL】十五、OpenGL 绘制三角形 ( 绘制 GL_TRIANGLE_FAN 三角形扇 )

文章目录 一、绘制 GL_TRIANGLE_FAN 三角形1、绘制 3 个点的情况2、绘制 4 个点的情况3、绘制 5 个点的情况4、绘制 6 个点的情况 二、相关资源 一、绘制 GL_TRIANGLE_FAN 三角形 GL_TRIANGLE_FAN 的绘制规则是 , 以第 1 1 1 个点作为顶点 , 第 1 , 2 , 3 1,2,3 1,2,3 个点组…

Unity3D笔记十八 GL图像库

作者&#xff1a;PEPE 出处&#xff1a;http://pepe.cnblogs.com/ 1、绘制2D图像的时需要使用GL.LoadOrtho()方法来将图形映射到平面中。 2、所有绘制相关的内容都要写在OnPostRender()方法中。 3、有关GL图像库的脚本需要绑定到Hierarchy视图中Camera上&#xff0c;否则无法…

Mapbox GL插件之echartsLayer

Mapbox GL除了本身的api具有的功能以外&#xff0c;还能够集成各种开源的类库。 ECharts 是一个使用 JavaScript 实现的开源可视化库&#xff0c;涵盖各行业图表&#xff0c;百度公司开发的&#xff0c;同时其中也有一些地图的效果。 Mapbox GL的echarts插件&#xff0c;在gith…

【OpenGL】十八、OpenGL 绘制多边形 ( 绘制 GL_POLYGON 模式多边形 )

文章目录 一、绘制 GL_POLYGON 模式多边形二、多边形绘制顺序分析三、相关资源 一、绘制 GL_POLYGON 模式多边形 使用 glBegin(GL_POLYGON) 设置绘制多边形 , 不管有几个点 , 都按照指定的顺序连接起来 ; 注意 : 这些点组成的多边形必须是凸多边形 , 不能是凹多边形 ; 代码示例…

gl_FragCoord 的含义

gl_FragCoord 表示当前片元着色器处理的候选片元窗口相对坐标信息&#xff0c;是一个 vec4 类型的变量 (x, y, z, 1/w)&#xff0c; 其中 x, y 是当前片元的窗口坐标&#xff0c;OpenGL 默认以窗口左下角为原点&#xff0c; 在 着色器中通过布局限定符可以重新设定原点&#xf…

WebGL着色器内置变量gl_PointSize、gl_Position、gl_FragColor、gl_FragCoord、gl_PointCoord

WebGL着色器内置变量 WebGL中文教程网 本文是WebGL教程(电子书)的2.7节内容 着色器语言在GPU的着色器单元执行&#xff0c;javascript语言、C语言在CPU上执行&#xff0c;任何一种语言的语法规则&#xff0c;整体设计都和它执行的硬件有一定的关系&#xff0c;GPU和CPU执行程…

Unity画线之GL

上一篇中&#xff0c;SetPixel的方法&#xff0c;卡顿严重&#xff0c;暂未解决&#xff0c;又去看了原来的GL画线&#xff0c;自己画图思考了一下适配UI的问题&#xff0c;最终解决。 特此说明&#xff0c;GL画线功能&#xff0c;及Shader均为借鉴&#xff0c;自己做了优化。…

GL823K

下面是另一家SD/TF解码芯片的方案 ![](https://img-blog.csdnimg.cn/20210319145313645.png?x-oss-processimage/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80OTU3MDgwNA,size_16,color_FFFFFF,t_70 13030533945 VX

GL各个表结构总结

NewProgramer EBS GL表结构学习(转) gl_code_combinations&#xff1a;科目组合 字段名 含义 备注 code_combination_id 主键&#xff0c;科目编码ID&#xff0c;自动编号 segment1 分行代码 setgment2 是受益部门 segment3 科目代码 segment4 产品…

初识mapbox GL

一、概述 最近由于项目的需求&#xff0c;借此机会对mapbox GL做了一个系统的学习&#xff0c;同时也对整个学习过程做一个记录&#xff0c;一方面留作自用&#xff0c;另一方面也希望看到此文的人在学习mapbox GL的时候&#xff0c;能够有所启发、有所收获。 二、快速认识 …

支持Genero BDL 4gl语言的编辑器

内测版本出来啦。点此下载: FglDeveloper v1.0 →此版本已下架 还有bug,分享几张截图 模板产生器&#xff1a; 画面产生器&#xff1a; 编辑器各种变量提醒 详细功能小伙伴们下载后体验。

TOPGP5.3:导入jar包并在4GL中引用

查看环境$CLASSPATH 上传引用到的JAR包到以下目录 /u1/topprod/tiptop/ds4gl2/bin/javaad/jar 以上为GP5.3目录&#xff0c;其他版本系统可根据查看到的$CLASSPATH上传到相应目录设置环境变量 GP5.3系统中&#xff0c;$CLASSPATH环境变量的设置存在下图文件中&#xff1a; …

4gl调用WEB API,实现JSON传递(Demo)

测试环境: GP5.25 , fjs版本2.32,解析json所需要的jar依赖包 (PS: 如果没有记错是fjs2.32版本及以上才支持java bridge,所以GP 5.25以下的同学就不要用这种方式去测试) 测试内容: 利用此fjs版本对java bridge的支持,实现4gl调用WEB API,实现json传递 测试步骤如下: 1.下载本…

给大家展示一下4gl编辑器

&#xff08;正式版已发布点击下载&#xff09;特地为编辑器开发内置语法解析器&#xff0c;将在代码编辑过程中实时提示代码错误&#xff0c;并且错误提示都是中文显示(楼主英文太垃圾只有做中文了),不再需要频繁上传服务器了哦&#xff0c; 经过楼主努力已经把所有的内置函数…

【实习之T100开发】Genero FGL (TIPTOP4GL) 学习笔记(1)

Genero FGL 学习 Genero FGL 简介Genero FGL 开发&#xff08;编译、连接、执行&#xff09;第一个程序 Hello World变量与运算符变量定义&#xff08;DEFINE&#xff09;预定义变量变量集合&#xff08;RECORD &#xff09;数据结构&#xff08;TYPE&#xff09;变量赋值&…

win10忘记密码重置密码,一行代码帮你解决

步骤如下&#xff1a; 1.右击windows图标&#xff0c;选择进入Windows PowerShell(管理员&#xff09; 2.敲入代码net user 加上你的用户名和新密码&#xff0c;Ok&#xff0c;问题解决&#xff0c;你就可以用你的新密码登陆了

win10 重置登录账户密码

在登陆界面按下强制关机&#xff08;重复3次&#xff09;。注意&#xff1a;不能进到登陆界面 出现下图时&#xff0c;选择“高级选项”&#xff1a; 选择“疑难解答”&#xff1a; 选择“高级选项”&#xff1a; 选择“命令提示符”&#xff1a; 在管理员窗口输入diskpart 回车…

服务器2008系统设置密码,win2008服务器设置密码

win2008服务器设置密码 内容精选 换一换 修改服务IP地址&#xff0c;并且将DNS地址指向本机&#xff0c;然后修改计算机名为server。安装AD域服务之后&#xff0c;机器名称会自动变成“主机名域名”的形式&#xff0c;例如server.huawei.com。在命令行下输入dcpromo.exe &#…