【MySQL】搞懂mvcc、read view:MySQL事务原理深度剖析

        前言:本节内容是事务里面最难的一部分, 就是理解mvcc快照读和read view。这两个部分需要了解隔离性里面的四种隔离级别。 博主之前讲过,但是担心友友们不了解, 所以这里开头进行了复习。 下面开始我们的学习吧!

        ps:本节内容很难, 希望友友们沉下心认真学习哦!

目录

一致性

三种并发方式的安全隐患

三个隐藏字段

Undo日志

mvcc多版本控制

read view

 RC VS RR的本质区别


我们先来回忆一下上接内容讲解的隔离性的四种隔离级别, 以及并发产生的问题:

  • 脏读:一个事务在执行中,读到另一个事务的更新但是还没有commit的数据, 这种情况叫做脏读。
  • 不可重复读:一个事务提交前和提交后, 另一个事务select查到的数据结果是不一样的。 也就是说一个事务一直select, 结果某一次select的时候, 数据突然变了。
  • 幻读:读者读者可能多出来了一条数据,就类似于出现了幻觉。 
  • 读未提交: 会导致脏读,不可重复读, 幻读现象。
  • 读提交 :解决了脏读, 但是有不可重复读, 幻读。
  • 可重复单独: 解决了脏读, 不可重复读, 但是有些数据库的insert会有幻读现象(mysql解决了幻读)
  • 串行化:加锁, 不进行并发执行, 没有上面的问题。

一致性

        事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务成功提交的结果时,数据库处于一种一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中断,而改变未完成的事务对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确的状态。因此一致性是通过原子性保证的。

三种并发方式的安全隐患

  • 读读并发:不存在任何问题,不需要并发控制。
  • 读写并发:有线程安全问题,可能造成事务隔离性问题可能遇到脏读,幻读,不可重复读
  • 写写并发:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失 

        读写并发,也就是一个事务在写,一个事务在读。要保证读到的是原来的,和另一个事务写的不一样。
        这个使用的是一种无锁的并发控制MVCC。

  • 1、每个事务都要有自己的事务ID,可以根据事务的大小,来决定事务到来的先后顺序。
  • 2、mysqld可能会面临处理多个事务的情况。事务在使用者的角度看来是原子的,但是本质上是有过程的。所以就证明事务也有自己的生命周期。一》mysqld要对事务进行管理:先描述,再组织。事务在我看来,mysqld中一定是对应的一个或者一套结构体对象/类对象
  • 3、所以,事务也要有自己的结构体。 

下面我们都在讲解mvcc以及版本链, 很重要, 很难理解, 需要反复观看。

三个隐藏字段

其实我们之前在建表的时候, mysql也会给我们添加三个默认的,隐藏的字段:

  • DB_TRX_ID:6byte,每一条数据后面都要有一个属性,就是DB_TRX_ID,无论是单sql,但是我们手动begin起的事务。都算是事务,都有自己的事务ID,而DB_TRX ID,就是最后操作这条数据的事务ID。
  • DB_ROLL_PTR:在MySQL中,对于某一行数据来说,我们要修改他,并不是直接就修改了。 mysql要先将数据保存一份,然后再改 这样的话,我们改完之后,我们也知道未来这个字段历史是谁。所以,为了保证能够找到这个最后的历史信息,就有了这个DB_ROLL_PTR。
  • DB_ROW_ID:隐藏的自增ID,如果数据库表中没有主键,In        noDB会自动以DB_ROW_ID产生一个聚族索引。大小6byte

Undo日志

        上面的红框框就是操作系统, 然后上层就是用户层。里面有我们的mysql的一个buffer pool , 然后undo日志就是在这个buffer pool里面。undo log其实就是mysql里面的一段内存缓冲区。作用就是用来记录一些mysql里面事务的回滚操作等。

        假如现在有一行数据如下:

        这个数据我们知道是在b+树的叶子节点上面。(这个b+树也在buffer pool里面),现在有一个事务10, 想要对这行数据进行修改, 将名字张三改成李四。       

        因为要修改, 所以先将这条记录加锁, 然后将这条数据记录复制一份到undo log里面:

        然后就开始修改, 现存数据name改成李四, 事务ID改成10, 然后回滚指针指向刚刚拷贝到undo log里面的数据。这就叫做历史版本。

        最后commit, 释放锁。 就行了。 (注意,这里示例是写写并发, 所以串行化, 加锁。)

        然后现在又来了一个事务11,也要进行修改, 要将这个age修改成28. 那么就和上面一样, 先拷贝一份数据到undo log里面。

        然后修改记录:

        所以, 我们就有了一个历史版本链, 如果未来我们不想修改了, 后悔了, 就可以把undo log里面的历史数据拿出来。 

mvcc多版本控制

        我们把上面的版本链, 这种多版本控制就叫做mvcc多版本控制。而里面的一个一个的版本的数据, 我们就叫做快照。 

        另外, undo log是一个临时缓冲区。 他管理的是一个事务内所形成的版本链条。 什么意思? 就是说, 一个事务里面, 我们多次修改,多次update,才会形成版本链, 当事务commit的时候, undo log 就会被清空, 也就是free掉。 同样的, delete数据也能形成版本链, 因为delete的本质是把改行数据的"flag“置为删除。 所以也可以形成版本链。

        问题是!insert可以形成版本链吗?——其实,insert可以理解为是没有版本链的,但是insert对应的数据也要放到undo log里面。但是如果一旦commit, 这个insert对应的版本连也就可以被清空了。 而对于undate和delete来说就不一定, 因为可能也有其他的事务在访问这一条记录。 

        我们上面说的是update和delete, 以及insert。 那么select呢?我们知道, update和delete的对象一定是最新数据, 历史数据他们没有权限修改。 那么select我们怎么知道我们读取的是最新数据还是历史数据呢? 所以就有了当前读和快照读的概念。 

  •         当前读:读取最新的记录, 就是当前读, 增删改,都叫做当前读。select也可能当前读。
  •         快照读:读取历史版本,就叫做快照读。

        那么现在再来看我们之前的读写操作。 我们做过实验, 看到的是两个事务, 一个写一个读。 然后两个事务看到的数据是不同的数据。——就是因为我们的读取, 读取的是历史的数据。 而写,写的是当前的数据。 两个事务在访问不同的位置, 就不需要进行加锁, 所以就不会发生并发的问题, 我们就可以并发进行读写操作。所以, 这就是之前我们为什么能够读写并发的原因!!! 

        所以, 隔离性, 本质上是数据上的隔离, 也是版本上面的隔离。这就是不同的隔离级别让我们看到不同的版本!!!

        所以, 隔离, 隔离性本质上使用mvcc实现的,我们的事务回滚利用的也是利用mvcc版本控制的。

read view

        read view我们讨论的是rc和rr这两种隔离级别。read view就是事务进行快照读操作的时候生产的读视图,在该事务执行快照读的那一刻,会生成数据库当前的一个快照,记录并维护系统当前活跃事务的ID。(当每个事务开启时,都会被分配给一个ID,这个ID是递增的。所以最新的事务的ID更大)

        read view是一个类,与事务的关系就类似于进程地址空间和PCB。两个数据结构之间使用指针连接起来。

        当我们进行快照读的时候,对该记录创建一个read view读视图。然后对它填充数据,作为条件。 用来判断当前事务能够看到哪个版本的数据,既可能是当前数据, 也可能是undo log里面的历史数据。下面看一下这个read view结构体:

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。    ids_t m_ids; //创建视图时的活跃事务id列表。trx_id_t m_low_limit_no; bool m_closed;  //视图是否关闭//....省略       
};

        上面重要的是两个水位线 , 事务id。m_ids就是当前正在打开的事务id列表。

现在一张图我们来理解read view:

        在这张图里面,先看已经提交的事务。 已经提交的事务,就是说我们当前的事务read view的时候,如果当前事务ID和我们看到的版本链里面的事务ID一样, 那么就说明我们可以看到这个版本的数据。如果版本链里面的事务ID比m_ids里面最小的事务ID都小, 那么也能看到。 因为ID小, 代表事务启动的早, 那么后启动的事务就应该能看到先启动的事务。

        再看快照后来的事务:我们说low_limit_id是当前事务read view后能看到的最晚启动的一个事务。 那么如果版本链里面的事务大于这个low_limit_id, 就不应该看到这个版本的数据。为什么会出现这种情况, 因为事务一定有先有后, 后来的事务ID一定大!

        然后就是看正在操作的事务, 这里要注意的是,正在操作的事务不一定是连续的。 因为虽然我们说ID是递增的, 但是事务的执行时间是不同的, 有的事务很快就跑完了, 有的事务很慢才能跑完。 所以就会出现图中的情况。 而我们形成快照后m_ids里面的版本如果和某个版本的数据相同,就说明这个ID的事务还是活跃的, 没有commit, 那么就不能看到。如果没有, 那么就能看到。 

        另外,我们还要注意的是!read view是事务可见性的一个类,不是事务创建出来, 就有read view, 而是这个已经存在的事务, 首次进行快照读的时候,mysql形成read view!

 RC VS RR的本质区别

        有了上面的read view的知识点之后, 我们就能谈RC和RR的本质区别了。我们谈论RC和RR的区别, 需要使用一个例子, 现在我们做几个实验。 
        首先第一个实验:

        这是account表。 并且此时的隔离级别是可重复读。然后我们两边都开启事务:

然后两边都进行快照读, 记录当前的read view:

接下来我们左边修改数据,然后commit:

然后我们查一下右边:

很显然, 因为是可重复读, 所以数据没有变化。 这也符合我们的预期。

现在我们来看一下实验二:

        同样是隔离级别为可重复读。 但是这次右边不进行快照读。 而是等到左边commit之后,在进行快照读:

查右边:

我们会发现, id = 4这个字段被修改了。 也就是说我们的右边看到了左边的修改。 

        这里为什么会发生这种情况, 是因为我们的read view是在快照读的时候就生成, 然后RR隔离界别的read view只会生成一次。 就是第一次快照读的时候。 所以在第一个实验中, 我们一开始就使用了快照读。 此时m_ids显示左右两个事务都是活跃事务, 那么我们进行判断的时候, 右边的事务对于左边事务修改的版本就看不见。 

        实验二的时候, 我们一开始没有快照读, 而是等到左边事务commit后才进行快照读。那么右边事务的m_ids里面就一定没有左边事务的ID, 并且,因为左边事务启动在右边事务快照读之前所以左边事务的事务ID也不可能比右边事务的事务的low_limt_id大。 那么这个时候左边事务的事务ID只有两种情况, 一种情况是比右边事务的up_limit_id还要小。或者是在up_limit_id和low_limit_id之间但是没在m_ids里面。 这两种情况下右边事务都可以看到左边事务的修改版本!!!所以右边事务就能看到左边事务的修改数据版本!!!

        上面是RR。RC就好说了, RR是只能生成一次read view, 只能进行一次快照。 后面的事务无论做什么修改, 都要根据这一个快照进行判断能不能够看到这些版本数据。 但是RC每次进行快照读都能生成快照, 每次快照读都生成快照, 那么low_limit_id就永远是当前系统下最大的事务ID。 所以就能保证只要其他事务commit了, 其他事务不是活跃事务了, 那么就只剩下了在up_limit_id和low_limit_id之间但是没在m_ids里面这两种情况。 也就一定能看到了。 所以这就是RC的形成原因。  这就是RR和RC的根本区别!

 

——————以上就是本节全部内容哦, 如果对友友们有帮助的话可以关注博主, 方便学习更多知识哦!!!    

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

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

相关文章

GXUOJ-算法-第四次作业(圆排列、连续邮资、n皇后、符号三角形)

1.圆排列 问题描述 GXUOJ | 圆排列 代码解答 #include<bits/stdc.h> using namespace std;int n; int r[1000]; double calculate(int r[],int n,double minL){double x,y;double sum0;for(int i0;i<n;i){int x,y;xr[i];yr[i1];//sumsqrt((xy)*(xy)-(x-y)*(x-y));s…

c++最大公约数和最小公倍数的深入剖析

目录 一、概念基础 二、常见算法及深度解析 1. 辗转相除法&#xff08;欧几里得算法&#xff09; 2. 更相减损术 3. 结合辗转相除法和更相减损术&#xff08;优化算法&#xff09; 三、应用场景全面举例 1. 化简分数 2. 判断互质关系 一、什么是最小公倍数 二、求最小…

【PCIe 总线及设备入门学习专栏 4.5 -- PCIe Message and PCIe MSI】

文章目录 PCIe Message 与 MSIPCIe Message 和 MSI 的作用与关系MSI 的配置与寄存器MSI 和 ARM GIC 的关系示例&#xff1a;MSI 在 ARM GIC 的实际应用总结 PCIe Message 与 MSI 本文将介绍 PCIe message 的作用以及message 与 MSI 的关系&#xff0c;再介绍 MSI 如何配置以及…

每天40分玩转Django:Django类视图

Django类视图 一、知识要点概览表 类别知识点掌握程度要求基础视图View、TemplateView、RedirectView深入理解通用显示视图ListView、DetailView熟练应用通用编辑视图CreateView、UpdateView、DeleteView熟练应用Mixin机制ContextMixin、LoginRequiredMixin理解原理视图配置U…

IndexOf Apache Web For Liunx索引服务器部署及应用

Apache HTTP Server 是一款广泛使用的开源网页服务器软件,它支持多种协议,包括 HTTP、HTTPS、FTP 等 IndexOf 功能通常指的是在一个目录中自动生成一个索引页面的能力,这个页面会列出该目录下所有的文件和子目录。比如网上经常看到的下图展现的效果,那么接下来我们就讲一下…

C++的第一个程序

前言 在学习c之前&#xff0c;你一定还记得c语言的第一个程序 当时刚刚开始进行语言学习 因此告诉到&#xff0c;仅仅需要记住就可以 #include <stdio.h>int main(){printf("Hello World");return 0; }而对于c中的第一个程序&#xff0c;似乎有所变化 C的…

ipad如何直连主机(Moonlight Sunshine)

Windows 被连接主机&#xff08;Windows&#xff09; 要使用的话需要固定ip&#xff0c;不然ip会换来换去&#xff0c;固定ip方法本人博客有记载Github下载Sunshine Sunshine下载地址除了安装路径需要改一下&#xff0c;其他一路点安装完成后会打开Sunshine的Web UI&#xff…

mac docker部署jar包流程

mac docker部署jar包流程 默认服务器已经准备好了相关的准备工作&#xff0c;如&#xff1a;docker&#xff0c;docker内安装所需软件数据库&#xff0c;jdk等&#xff0c;将要部署等jar包。 1:将jar 包上传到服务器目录下&#xff1a;/usr/local/service (没有目录可以自己创建…

Qt -初识

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【暂无】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 文章目录 &#x1f4da; 前言&#x1f6e0;️ 搭建环境&#x1f4c2; 新建项目&#x1f4dd; 初始代码理解main.cppwidget.hwidget.cppwidget.uiHelloWorld.pro&#x1f6e…

2024.12.30(多点通信)

作业&#xff1a; 1、将广播发送和接收端实现一遍&#xff0c;完成一个发送端发送信息&#xff0c;对应多个接收端接收信息实验。 发送端 #include <myhead.h>#define PORT 8888 #define IP "192.168.124.255"int main(int argc, const char *argv[]) {//1、…

点进CSS选择器

CSS 1.直接在标签的style属性进行设置(行内式) //写在数据单元格td标签内的stytle&#xff0c;设置color颜色和font-size字体大小&#xff1b; <td rowspan"3" style"color: red;font-size: 12px;">Web技术与应用</td> 2.写在head标签中的…

UE5材质节点BumpOffset

BumpOffset 凹凸偏移&#xff0c;可以让材质显示视差偏移的效果 Coordinate是UV&#xff0c;Height是凹凸偏移高度&#xff0c;HeightRatioInput用来控制高度比

仙盟系统开发——启动app失败

var 返回 仙盟使者.Cyber_CallApp(VOAPP, 命令, 携带);

LabVIEW声波谐振管自动化测量系统

开发了一种基于LabVIEW的声波谐振管自动化测量系统。该系统利用LabVIEW的强大功能&#xff0c;实现了对声波谐振频率的精确测量&#xff0c;提高了实验数据的采集效率和准确性。系统主要应用于物理教学和科研中&#xff0c;用于研究声波在谐振管中的传播特性。 项目背景 传统的…

学习笔记:使用 pandas 和 Seaborn 绘制柱状图

学习笔记&#xff1a;使用 pandas 和 Seaborn 绘制柱状图 前言 今天在使用 pandas 对数据进行处理并在 Python 中绘制可视化图表时&#xff0c;遇到了一些关于字体设置和 Seaborn 主题覆盖的小问题。这里将学习到的方法和注意事项做个总结&#xff0c;以便之后的项目中可以快…

【Linux】进程间通信-> 共享内存

共享内存原理 在C语言/C中&#xff0c;malloc也可以在物理内存申请空间&#xff0c;将申请的物理内存空间通过页表映射到进程地址空间&#xff0c;将内存空间的起始地址&#xff08;虚拟地址&#xff09;返回&#xff0c;进而进程可以使用虚拟地址通过页表映射到物理内存的方式…

【yolov5】实现FPS游戏人物检测,并定位到矩形框上中部分,实现自瞄

介绍 本人机器学习小白&#xff0c;通过语言大模型百度进行搜索&#xff0c;磕磕绊绊的实现了初步效果&#xff0c;能有一些锁头效果&#xff0c;但识别速度不是非常快&#xff0c;且没有做敌友区分&#xff0c;效果不是非常的理想&#xff0c;但在4399小游戏中爽一下还是可以…

【Maven】Maven打包机制详解

Maven打包的类型&#xff1f; 以下是几种常见的打包形式&#xff1a; 1、jar (Java Archive) 用途&#xff1a;用于包含 Java 类文件和其他资源&#xff08;如属性文件、配置文件等&#xff09;的库项目。特点&#xff1a; 可以被其他项目作为依赖引用。适合创建独立的应用程…

MySQLOCP考试过了,题库很稳,经验分享。

前几天&#xff0c;本人参加了Oracle认证 MySQLOCP工程师认证考试 &#xff0c;先说下考这个证书的初衷&#xff1a; 1、首先本人是从事数据库运维的&#xff0c;今年开始单位逐步要求DBA持证上岗。 2、本人的工作是涉及数据库维护&#xff0c;对这块的内容比较熟悉&#xff…

MySQL数据导出导出的三种办法(1316)

数据导入导出 基本概述 目前常用的有3中数据导入与导出方法&#xff1a; 使用mysqldump工具&#xff1a; 优点&#xff1a; 简单易用&#xff0c;只需一条命令即可完成数据导出。可以导出表结构和数据&#xff0c;方便完整备份。支持过滤条件&#xff0c;可以选择导出部分数据…