MySQL性能优化(五)undo log是如何实现MVCC的?

之前我们最开始的几篇文章就讲过,你除了写redolog日志还必须要写undo log日志,这个undo log日志是至关重要的,没有他,你根本都没办法回滚事务!

1.事务

1.1.多线程并发执行多个事务

对于我们的业务系统去访问数据库而言,他往往都是多个线程并发执行多个事务的,对于数据库而言,他会有多个事务同时执行,可能这多个事务还会同时更新和查询同一条数据,所以这里会有一些问题需要数据库来解决,如下图:
在这里插入图片描述
每个事务都会执行各种增删改查的语句,把磁盘上的数据页加载到buffer pool的缓存页里来,然后更新
缓存页,记录redo log和undo log,最终提交事务或者是回滚事务,多个事务会并发干上述一系列事情。
多线程并发执行多个事务带来的问题如下。

1.1.1脏写

  1. 事务A和事务B同时在更新一条数据,事务A先把他更新为A值,事务B紧接着就把他更新为B值。
  2. 此时事务A突然回滚了,那么就会用他的undo log日志去回滚。
  3. 事务B看到的场景,就是自己明明更新了,结果值却没了。

本质就是事务B去修改了事务A修改过的值,但是此时事务A还没提交,所以事务A随时会回滚,
导致事务B修改的值也没了,这就是脏写
在这里插入图片描述

1.1.2.脏读

  1. 事务A更新了一行数据的值为A值,此时事务B去查询了一下这行数据的A值
  2. 事务B此时拿到刚查出来的A值在做一些业务处理
  3. 事务A回滚了事务,导致刚才更新的A值没了,此时那行数据的值回滚为NULL值
  4. 事务B此时再次查询那行数据的值,看到的居然此时是NULL值

本质其实就是事务B去查询了事务A修改过的数据,但是此时事务A还没提交,所以事务A随时会回滚导致事务B再次查询就读不到刚才事务A修改的数据了!这就是脏读。
在这里插入图片描述
无论是脏写还是脏读,都是因为一个事务去更新或者查询了另外一个还没提交的事务更新过的数据。因为另外一个事务还没提交,所以他随时可能会反悔会回滚,那么必然导致你更新的数据就没了,或者你之前查询到的数据就没了,这就是脏写和脏读两种坑爹场景。

1.1.3.不可重复读

假设我们有一个事务A开启了,在这个事务A里会多次对一条数据进行查询
假设是事务A只能在事务B提交之后读取到他修改的数据(避免脏读)

  1. 假设缓存页里一条数据原来的值是A值,此时事务A开启之后,第一次查询这条数据,读取到的就是A值
  2. 事务B更新了那行数据的值为B值,同时事务B立马提交了,然后事务A此时可是还没提交!
  3. 事务A执行期间第二次查询数据,此时查到的是事务B修改过的值,B值,因为事务B已经提交了,所以事务A可以读到的了。
    在这里插入图片描述
    其实要说没问题也可以是没问题,毕竟事务B和事务C都提交之后,事务A多次查询查到他们修改的值,是ok的。

但是你要说有问题,也可以是有问题的,就是事务A可能第一次查询到的是A值,那么他可能希望的是在
事务执行期间,如果多次查询数据,都是同样的一个A值,他希望这个A值是他重复读取的时候一直可以
读到的!他希望这行数据的值是可重复读的!

这个问题简单来说,就是一个事务多次查询一条数据,结果每次读到的值都不一样,这个过程中可能别的事务会修改这条数据的值,而且修改值之后事务都提交了,结果导致人家每次查到的值都不一样,都查到了提交事务修改过的值,这就是所谓的不可重复读。

1.1.4.幻读

  1. 个事务A,先发送一条SQL语句,他一开始查询出来了10条数据
  2. 事务B往表里插入了几条数据,而且事务B还提交了,此时多了2行数据出来
  3. 事务A此时第二次查询,还是那条SQL,还是那个查询条件,但是查询出来的数据是12条

幻读指的就是你一个事务用一样的SQL多次查询,结果每次查询都会发现查到了一些之前没看到过的数
据注意,幻读特指的是你查询到了之前查询没看到过的数据!此时就说你是幻读了。
在这里插入图片描述

1.2.SQL标准四种事务隔离

这4种级别包括了:read uncommitted(读未提交),read committed(读已提交),repeatable read(可重复读),serializable(串行化)

事务级别可能出现的问题备注
读未提交脏读,不可重复读,幻读
读已提交RC不可重复读,幻读
可重复读 RR幻读
串行化性能降低根本就不允许你多个事务并发执行

1.3.MySQL事务隔离

但是要注意的一点是,MySQL默认设置的事务隔离级别,是RR级别的(一般数据库是RC)。并且MySQL的RR级别的语义跟SQL标准的RR级别不同的,毕竟SQL标准里规定RR级别是可以发生幻
读的,但是MySQL的RR级别就避免了幻读。

MySQL里执行的事务,默认情况下不会发生脏写、脏读、不可重复读和幻读的问题,事务的执行都是并行的,大家互相不会影响,我不会读到你没提交事务修改的值,即使你修改了值还提交了,我也不会读到的,即使你插入了一行值还提交了,我也不会读到的,总之,事务之间互相都完全不影响!

当然,要做到这么神奇和牛叉的效果,MySQL是下了苦功夫的,后续我们接着就要讲解MySQL里的
MVCC机制,就是多版本并发控制隔离机制,依托这个MVCC机制,就能让RR级别避免不可重复读和幻读的问题。但是一般来说,真的其实不用修改这个级别,就用默认的RR其实就特别好,保证你每个事务跑的时候都没人干扰,何乐而不为呢

2.undo log

2.1.undo log 日志结构

在这里插入图片描述

  1. 一条日志必须得有自己的一个开始位置,这个没什么好说的
  2. 那么主键的各列长度和值是什么意思?大家都知道,你插入一条数据,必然会有一个主键!如果你自己指定了一个主键,那么可能这个主键就是一个列,比如id之类的,也可能是多个列组成的一个主键,比如“id+name+type”三个字段组成的一个联合主键,也是有可能的。所以这个主键的各列长度和值,意思就是你插入的这条数据的主键的每个列,他的长度是多少,具体的值是多少。即使你没有设置主键,MySQL自己也会给你弄一个row_id作为隐藏字段,做你的主键。
  3. 接着是表id,这个就不用多说了,你插入一条数据必然是往一个表里插入数据的,那当然得有一个表id,记录下来是在哪个表里插入的数据了。
  4. undo log日志编号,这个意思就是,每个undo log日志都是有自己的编号的。而在一个事务里会有多个SQL语句,就会有多个undo log日志,在每个事务里的undo log日志的编号都是从0开始的,然后依次递增。
  5. undo log日志类型,就是TRX_UNDO_INSERT_REC,insert语句的undo log日志类型就是这个东西。
  6. undo log日志的结束位置,这个自然也不用多说了,他就是告诉你undo log日志结束的位置是什么。

2.2.undo log 版本链

每条数据其实都有两个隐藏字段,一个是trx_id,一个是roll_pointer,这个trx_id就是最近一次更新这条数据的事务id,roll_pointer就是指向你了你更新这个事务之前生成的undo log。

  1. 假设有一个事务A(id=50),插入了一条数据,那么此时这条数据的隐藏字段以及指向的undo
    log如下图所示,插入的这条数据的值是值A,因为事务A的id是50,所以这条数据的txr_id就是50,roll_pointer指向一个空的undo log,因为之前这条数据是没有的
  2. 事务B跑来修改了一下这条数据,把值改成了值B,事务B的id是58,那么此时更新之会生成一个undo log记录之前的值,然后会让roll_pointer指向这个实际的undo log回滚日志,这个undo log就记录你更新之前的那条数据的值。
  3. 事务C又来修改了一下这个值为值C,他的事务id是69,此时会把数据行里的txr_id改成69,然后生成一条undo log,记录之前事务B修改的那个值。

在这里插入图片描述
多个事务串行执行的时候,每个人修改了一行数据,都会更新隐藏字段txr_id和roll_pointer,同时之前多个数据快照对应的undo log,会通过roll_pinter指针串联起来,形成一个重要的版本链

2.3.ReadView

生成readview时机
RC隔离级别:每次读取数据前,都生成一个readview;
RR隔离级别:在第一次读取数据前,生成一个readview;

当我们执行一个事务的时候,就给你生成一个ReadView,里面比较关键的东西有4个
readview四个主要元素
m_ids:表示在生成readview时,当前系统中活跃的读写事务id列表;
min_trx_id:表示在生成readview时,当前系统中活跃的读写事务中最小的事务id,也就是m_ids中最小的值;
max_trx_id:表示生成readview时,系统中应该分配给下一个事务的id值;
creator_trx_id:表示生成该readview的事务的事务id;
readview判断版本链
有了readview,在访问某条记录时,按照以下步骤判断记录的某个版本是否可见

  1. 如果被访问版本的trx_id,与readview中的creator_trx_id值相同,表明当前事务在访问自己修改过的记录,该版本可以被当前事务访问;
  2. 如果被访问版本的trx_id,小于readview中的min_trx_id值,表明生成该版本的事务在当前事务生成readview前已经提交,该版本可以被当前事务访问;
  3. 如果被访问版本的trx_id,大于readview中的max_trx_id值,表明生成该版本的事务在当前事务生成readview后才开启,该版本不可以被当前事务访问;
  4. 如果被访问版本的trx_id,值在readview的min_trx_id和max_trx_id之间,就需要判断trx_id属性值是不是在m_ids列表中?
    如果在:说明创建readview时生成该版本的事务还是活跃的,该版本不可以被访问
    如果不在:说明创建readview时生成该版本的事务已经被提交,该版本可以被访问;

通过undo log多版本链条,加上你开启事务时候生产的一个ReadView,然后再有一个查询的时候,根
据ReadView进行判断的机制,你就知道你应该读取哪个版本的数据。而且他可以保证你只能读到你事务开启前,别的提交事务更新的值,还有就是你自己事务更新的值。假如说是你事务开启之前,就有别的事务正在运行,然后你事务开启之后 ,别的事务更新了值,你是绝对读不到的!或者是你事务开启之后,比你晚开启的事务更新了值,你也是读不到的!通过这套机制就可以实现多个事务并发执行时候的数据隔离。

2.4.基于ReadView实现RR事务

在MySQL中让多个事务并发运行的时候能够互相隔离,避免同时读写一条数据的时候有影响,是依托undo log版本链条和ReadView机制来实现的。

第一步:首先我们还是假设有一条数据是事务id=50的一个事务插入的,同时此时有事务A和事务B同时在运行,事务A的id是60,事务B的id是70。
在这里插入图片描述
第二步:这个时候,事务A发起了一个查询,他就是第一次查询就会生成一个ReadView
在这里插入图片描述

第三步:事务A基于这个ReadView去查这条数据,通提供过对比这条数据的事务id和ReadView比较
在这里插入图片描述

第四步:事务B此时更新了这条数据的值为值B,此时会修改trx_id为70,同时生成一个undo log版本链,而且关键是事务B此时他还提交了,也就是说此时事务B已经结束了,如下图所示。
在这里插入图片描述
这个时候大家思考一个问题,ReadView中的m_ids此时还会是60和70吗?那必然是的,因为ReadView一旦生成了就不会改变了,这个时候虽然事务B已经结束了,但是事务A的ReadView里,还是会有60和70两个事务id。

第五步:事务A继续查询数据,结果发现这条数据的trx_id为70,如果被访问版本的trx_id,值在readview的min_trx_id和max_trx_id之间,就需要判断trx_id属性值是不是在m_ids列表中,而我们发现我们的ReadView中,刚好有70。说明创建Readview时生成该版本的事务B还是活跃的,该版本不可以被访问。
因此这个时候只能顺着指针往历史版本链条上,找到下面一条数据,trx_id为50,是小于ReadView的min_trx_id的,说明在他开启查询之前,就已经提交了这个事务了,所以事务A是可以查询到这个值的,此时事务A查到的是原始值。
在这里插入图片描述

你事务A多次读同一个数据,每次读到的都是一样的值,除非是他自己修改了值,否则读到的一直会一
样的值。不管别的事务如何修改数据,事务A的ReadView始终是不变的,他基于这个ReadView始终看到的值是一样的!

2.4.RR事务解决幻读

接着我们来看看幻读的问题他是如何解决的。假设现在事务A先用select * from x where id>10来查
询,此时可能查到的就是10条数据,而且读到的是这条数据的原始值的那个版本。具体原因同上

如果被访问版本的trx_id,值在readview的min_trx_id和max_trx_id之间,就需要判断trx_id属性值是不是在m_ids列表中?
如果在:说明创建readview时生成该版本的事务还是活跃的,该版本不可以被访问
如果不在:说明创建readview时生成该版本的事务已经被提交,该版本可以被访问;

第一步:现在有一个事务C插入了一条数据
在这里插入图片描述
第二步:此时事务A再次查询,此时会发现符合条件的有12条数据,10条是原始值那个数据,2条是事务C插入的那条数据,但是事务C插入的那条数据的trx_id是80,这个80是大于自己的ReadView的max_trx_id的,说明是自己发起查询之后,这个事务才启动的,所以此时这条数据是不能查询的。
因此事务A本次查询,还是只能查到原始值10条数据,如下图。
在这里插入图片描述
所以大家可以看到,在这里,事务A根本不会发生幻读,他根据条件范围查询的时候,每次读到的数据
都是一样的,不会读到人家插入进去的数据,这都是依托ReadView机制实现的。

3.MVCC机制

multi-version concurrent control,就是多版本并发控制机制。
MySQL实现MVCC机制的时候,是基于undo log多版本链条+ReadView机制来做的,默认的RR隔离级别,就是基于这套机制来实现的,依托这套机制实现了RR级别,除了避免脏写、脏读、不可重复读,还能避免幻读问题。因此一般来说我们都用默认的RR隔离级别就好了。

这就使得别的事务可以修改这条记录,反正每次修改都会在版本链中记录。SELECT可以去版本链中拿记录,这就实现了读-写,写-读的并发执行,提升了系统的性能。

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

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

相关文章

Linux中Shell脚本--awk的用法

语法格式:awk [选项] ‘指令’ 操作文件 常用选项:-F 指定分隔符,分隔符用""引起来 -v:varvalue在awk程序开始之前指定一个值valu给变量var,这些变量值用于awk程序的BEGIN快 -f:后面跟一个保存…

linux下的shell脚本(基础)

Shell是一种脚本语言,那么,就必须有解释器来执行这些脚本,常见的脚本解释器有: bash:是Linux标准默认的shell。bash由Brian Fox和Chet Ramey共同完成,是BourneAgain Shell的缩写,内部命令一共有…

mysql rsync复制,mysql复制又同步

mysql复制再同步由于一个老旧系统没有使用LVM分区,导致mylvmbackup不能使用。为了重新全量同步数据库,发现rsync可以使用,并且锁住数据库的时间不长。1. 首先刷新数据库到文件flush tables with read lock;unlock tables;2. 执行rsync进行数据库同步/usr…

解决springboot中只支持get请求,无法支持post请求

解决springboot中只支持get请求,无法支持post请求 报错信息如下: 405 相关类如下: RestController RequestMapping public class HttpServiceController {Autowiredprivate HttpSecretReport httpSecretReport;Autowiredprivate HttpSecret…

mysql2005卸载步骤,二次安装mysql步骤

1.先将mysql服务停止,并关闭服务页面(否则后面报错:“指定的服务已经标记为删除”):打开“任务管理器”可以找到服务页面2.在控制面板对mysql进行删除。找到并将其卸载,如果没有直接跳过3.在注册表中删除相关目录。(使用组合键&qu…

Spring Boot 默认数据源 HikariDataSource_Spring Boot 中使用 Hikari

Spring Boot 默认数据源 HikariDataSource springboot2.x之后,系统的默认数据源由原来的的org.apache.tomcat.jdbc.pool.DataSource更改为com.zaxxer.hikari.HikariDataSource。 HikariDataSource 号称 Java WEB 当前速度最快的数据源,相比于传统的 C3…

matlab取出等高线上的数据,在Python或MATLAB中从等高线图中提取数据

这是一个小型的Matlab脚本,可以完成这项工作(使用一些GUI,在图的斜角处读取guidlines):%// Import the data:imdata importdata(your_picture_file);Gray rgb2gray(imdata.cdata);colorLim [-1 1]; %// this should be set manually%// Ge…

SpringBoot-默认数据源HikariDataSource对数据库操作及自动装配原理

默认数据源HikariDataSource对数据库操作 在创建项目时选择JDBC以及MySQL驱动&#xff0c;让SpringBoot自动装配所需组件 创建完成后默认的pom.xml文件如下 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.o…

matlab fgetl用法,Matlab fgetl strsplit 函数

函数功能&#xff1a;从文件中调用一行数据matlab中fgetl函数&#xff0c;并除去行末的换行符。语法格式&#xff1a;tline fgetl(fid)fid是通过fopen函数打开文件后得到的一个整型的文件标志。fgetl从这个文件中调用一行数据并丢弃其中的换行符。如果抓取成功tline容纳了调用…

HikariCP源码简洁剖析——HikariDataSource_HikariCP的使用和源码

文章目录HikariDataSource的作用源码剖析核心变量构造方法获取链接实例HikariCP的使用和源码简介HikariCP是什么&#xff1f;HikariCP 解决了哪些问题&#xff1f;为什么要使用 HikariCP&#xff1f;本文要讲什么&#xff1f;如何使用 HikariCP需求项目环境引入依赖编写 hikari…

MySQL如何创建沙箱,沙箱环境搭建 - osc_y8w65yuq的个人空间 - OSCHINA - 中文开源技术交流社区...

[toc]测试环境搭建沙箱环境&#xff1a;-------测试环境搭建基础配置&#xff1a;# 1、在沙箱环境下实名认证&#xff1a;https://openhome.alipay.com/platform/appDaily.htm?tabinfo# 2、电脑网站支付API&#xff1a;https://docs.open.alipay.com/270/105898/# 3、完成RSA密…

PHP单选框实现的方法,jQuery简单实现遍历单选框的方法

本文实例讲述了jQuery简单实现遍历单选框的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;1、问题背景&#xff1a;有四个单选框&#xff0c;分别为一年四季&#xff0c;现在需要判断是否选中&#xff0c;如果选中这个单选框&#xff0c;就将其值赋值给输入框2、…

String怎么转成BigDecimal_Java.math.BigDecimal类的使用

1.引言 浮点数值不适用于无法接受舍入误差的金融计算中。 例如&#xff0c;命令System.out.prmtln (2.0-1.1)将打印出0.8999999999999999&#xff0c;而不是人们想象的0.9 。 2.0-1.1的运算结果 这种舍入误差的主要原因是浮点数值采用二进制系统表示&#xff0c;而在二进制系统…

JTS Java空间几何计算、距离、最近点、subLine等计算

文章目录前言地理坐标系和投影坐标系地理坐标系投影坐标系地图投影墨卡托/Web墨卡托常见坐标系地理坐标系和投影坐标系互转EPSG:3857和EPSG:4326Java各坐标系之间的转换&#xff08;高斯、WGS84经纬度、Web墨卡托、瓦片坐标&#xff09;GeotoolsJTSvividsolutions和locationtec…

JTS学习笔记

简介 JTS由加拿大的VividSolutions公司开发&#xff0c;是一个用Java语言描述的几何拓扑套件&#xff0c;遵循OpenGIS的Simple Feature Specification&#xff0c;封装了2D几何类型和非常多的空间分析操作&#xff0c;而且包含了不少常见的计算几何算法实现。 JTS被广泛地应用…

填坑:Maven工程引用GeoTools依赖

这两天在做一个系统的后台&#xff0c;需要用到GeoTools做后端空间分析&#xff0c;记录一下自己遇到的问题。 项目通过Maven进行构建&#xff0c;参照文档Maven Quickstart添加GeoTools依赖和远程仓库地址。 <dependencies><dependency><groupId>org.geotoo…

墨卡托投影介绍

一、墨卡托投影 墨卡托投影&#xff0c;又称正轴等角圆柱投影&#xff0c;由荷兰地图学家墨卡托(G.Mercator)于1569年创拟。假设地球被套在一个圆柱中&#xff0c;赤道与圆柱相切&#xff0c;然后在地球中心放一盏灯&#xff0c;把球面上的图形投影到圆柱体上&#xff0c;再把…

GIS算法:可视化工具JTS TestBuilder

java、python、js都有可以引用的第三方包&#xff0c;实现GIS的空间算法。 java是jts&#xff0c;python是shapely&#xff0c;js是turf。 其中jts值得首先拥有&#xff0c;因为jts提供了一个界面工具JTS TestBuilder&#xff0c;可以在上面绘制图形&#xff0c;验证各种算法…

wkt区域围栏

API文档 http://shengshifeiyang.gitee.io/geotools-learning/ /** * 判断以x,y为坐标的点point(x,y)是否在geometry表示的Polygon中 * param x * param y * param geometry wkt格式 POLYGON((0 0, 10 0, 10 10, 0 10,0 0)) * return */ public static boolean withinGeo(doub…

oracle关联字段和序列,oracle(9) 序列和约束

序列 SEQUENCE也是数据库对象之一&#xff0c;作用&#xff1a;根据指定的规则生成一些列数字。序列通常是为某张表的主键提供值使用。主键&#xff1a;通常每张表都会有主键字段&#xff0c;该字段的值要求非空且唯一&#xff0c;使用该字段来确定表中的每一条记录。CREATE SE…