【MySQL】根据MVCC和Read View分析事务的四种隔离级别在读写场景分别是如何体现其隔离性的


目录

一、数据库并发的三种场景

二、读写场景的MVCC 

1、3个(4个)记录隐藏列字段

2、undo log(撤销日志)

3、模拟MVCC场景

3.1update场景

3.2delete场景

3.3insert

3.4select场景

4、Read View

5、RR和RC的区别

5.1当前读和快照读在RR级别下的区别

例一:root在jly修改前快照读

例二:root在jly修改后快照读

5.2MySQL对四种隔离级别的不同处理方式 

三、写写场景


一、数据库并发的三种场景

读-读 :不存在任何问题,也不需要并发控制

读-写 :有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读

写-写 :有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失

二、读写场景的MVCC 

多版本并发控制( MVCC )是一种用来解决 读-写冲突 的无锁并发控制。

为事务分配单向增长的事务ID,为每个修改保存一个版本,版本与事务ID关联,读操作只读该事务开始前的数据库的快照。 所以 MVCC 可以为数据库解决以下问题:

在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了并发读写的性能,同时解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题。

1、3个(4个)记录隐藏列字段

在创建表的时候,MySQL除了创建用户所需的列之外,还会创建3个记录隐藏列字段

DB_TRX_ID :6 byte,这一列记录了每一行最后一次修改的事务ID( 修改/插入 )。

DB_ROLL_PTR : 7 byte,回滚指针,指向这条记录的上一个版本(简单理解成,指向历史版本就行,这些数据一般在 undo log 中)

DB_ROW_ID : 6 byte,隐含的自增ID(隐藏主键),如果数据表没有主键, InnoDB 会自动以DB_ROW_ID产生一个聚簇索引

第四个隐藏列字段:实际还有一个标识该行数据是否删除的隐藏字段flag。

例如创建并插入一条数据,实际的表结构应该是这样的:

name

age

DB_TRX_ID

DB_ROLL_PTR

DB_ROW_ID

张三

20

创建该事务的ID

null

1(隐式主键)

2、undo log(撤销日志)

MySQL是以守护进程的方式,在内存中运行。undo log是MySQL中的一段内存缓冲区,用以保存日志数据。

3、模拟MVCC场景

3.1update场景

现有一个事务ID为10,对上方信息表进行update,将name由张三修改为李四:

1、因为要修改,所以要先给该记录加上行锁。

2、在修改之前,先将改行数据拷贝一份到undo log中(写时拷贝,原始数据在表中,拷贝的数据在undo log中,假设拷贝的数据地址是0XAA)

3、修改原始数据的同时,将隐藏字段DB_TRX_ID修改为10,将DB_ROLL_PTR回滚指针修改为0XAAAAAAAA

4、事务10commit提交,释放行锁

name

age

DB_TRX_ID

DB_ROLL_PTR

DB_ROW_ID

李四

20

10

0XAAAAAAAA

1(隐式主键)

此时又有一个事务11,需要对信息表的记录进行update,将李四那一行的年龄修改为30:

1、因为要修改,所以要先给该记录加上行锁。

2、同样的,将当前的表中的对应行拷贝一份到undo log,假设地址0XBBBBBBBB

3、修改原始数据的同时,将隐藏字段DB_TRX_ID修改为10,将DB_ROLL_PTR回滚指针修改为0XAAAAAAAA

name

age

DB_TRX_ID

DB_ROLL_PTR

DB_ROW_ID

李四

30

11

0XBBBBBBBB

1(隐式主键)

undo log中的一个个版本,被称为快照。除了版本链之外,还可以通过记录相反sql的方式,以备数据的回滚(比如delete数据,日志可以保存insert数据)

3.2delete场景

删除数据不是清空,只需将隐藏的flag标志位设置为删除即可。也可以形成版本。

3.3insert

insert是插入,只需插入时在undo log中记录其对应的delete语句即可,回滚时只需执行这些delete语句。如果当前的事务提交了,undo log将会删除这些备份数据。(update和delete可能有别的事务还在访问,commit之后不会立马就删除undo log的回滚数据)

3.4select场景

在MySQL的RR级别下,一个事务的写操作并不会影响另一个事务的读操作。增删改,都是对最新数据进行修改,但是读取,则可能需要读取历史的版本。

当前读:读取最新的记录,就是当前读。增删改,都叫做当前读,select也有可能当前读,比如select lock in share mode(共享锁), select for update

快照读:读取历史版本。快照读不会被加锁。

多个事务同时增删改的时候,是当前读,需要加锁,如果对select也加锁,那么隔离级别就是串行化。如果select是快照读,和增删改的当前读不影响,所以可以不用加锁,并行执行效率高。事务的隔离级别决定了select读取历史数据是当前读还是快照读。(read view是否更新)

那么如何保证不同的事务,看到不同的内容呢;先来的事务,应不应该看到后来的事务所作的修改呢?Read View进行可见性判断。

4、Read View

Read View在事务首次进行快照读的时候由MySQL生成,记录并维护系统当前活跃事务的ID。Read View 在 MySQL 源码中,就是一个类,本质是配合MVCC用来判断哪些快照我能看到,那些快照我看不到。

当某个事务执行select快照读的时候,MySQL会对其new一个对象,用其内部的条件来判断当前事务能够看到哪个版本的数据,可见的数据既能是当前最新的数据,也有可能是该行记录的 undo log 里面的某个版本的数据,这由隔离级别决定。

下面是 ReadView 简化的结构体:

class ReadView {
    // 省略...
    private:
    /** 高水位,大于等于这个ID的事务均不可见*/
    trx_id_t m_low_limit_id
    /** 低水位:小于这个ID的事务均可见 */
    trx_id_t m_up_limit_id;
    /** 创建该 Read View 的事务ID*/
    trx_id_t m_creator_trx_id;
    /** 创建视图时的活跃事务id列表*/
    ids_t m_ids;//ids_t集合类型 
    /** 配合purge,标识该视图不需要小于m_low_limit_no的UNDO LOG,
    * 如果其他视图也不需要,则可以删除小于m_low_limit_no的UNDO LOG*/
    trx_id_t m_low_limit_no;
    /** 标记视图是否被关闭*/
    bool m_closed;
    // 省略...
};
m_ids; //一张列表,用来维护Read View生成时刻,系统正活跃的事务ID
up_limit_id; //记录m_ids列表中事务ID最小的ID
low_limit_id; //ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1
creator_trx_id //创建该ReadView的事务ID

那么哪些数据能被事务读到,那些数据事务看不到呢?见下图:

总结一下:举个例子,我是学弟,我能看到比我早入学的学长的找工作的数据,但是学长看不到后入学的我找工作的数据。同理在形成快照的时候,我能看到:

已经提交的事务:

1、creator_trx_id(创建快照的事务ID)==DB_TRX_ID(undo log中最近一次修改该行的事务ID)

2、DB_TRX_ID(undo log中最近一次修改该行的事务ID)<up_limit_id(形成快照的m_ids列表中事务ID最小的ID)

创建快照时m_ids中的事务(活跃的事务ID):

1、快照中的事务ID不一定连续,快照中的事务ID范围为up_limit_id<=ID<low_limit_id。如果DB_TRX_ID(undo log中最近一次修改该行的事务ID)位于这个范围,但是快照表m_ids列表中并没有这个ID,那就说明该事务已经commit提交了,当前快照可以看到,如果m_ids列表有这个ID,说明当前快照这个ID的事务仍然活跃,不能被看到。

我看不到:

快照建立之后的新事物:

1、DB_TRX_ID(undo log中最近一次修改该行的事务ID)>=low_limit_id(快照生成时系统尚未分配的下一个ID)

如果查到不应该看到当前版本,接下来就是遍历下一个版本,直到符合条件。

name

age

DB_TRX_ID

DB_ROLL_PTR

DB_ROW_ID

张三

28

创建该事务的ID

null

1(隐式主键)

此时的undo log中的版本链:

在事务2对改行进行快照读的时候,按照undo log中快照的先后版本,依次遍历,得出本次我对该行的快照读应该读取到哪一个版本的快照。

5、RR和RC的区别

5.1当前读和快照读在RR级别下的区别

准备工作:

--将全局隔离级别设置为可重复读(需重启)
mysql> set global transaction isolation level REPEATABLE READ;
Query OK, 0 rows affected (0.00 sec)
--创建一张表
mysql> create table if not exists account(
    -> id int primary key,
    -> age int not null,
    -> name varchar(20) not null
    -> )ENGINE=InnoDB DEFAULT CHARSET=UTF8;
Query OK, 0 rows affected (0.26 sec)
--插入一条数据
mysql> insert into account values (1,18,'张三');
Query OK, 1 row affected (0.04 sec)

例一:root在jly修改前快照读

使用者:jly

--1、启动事务
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
--2、进行快照读
mysql> select* from account;
+----+-----+--------+
| id | age | name   |
+----+-----+--------+
|  1 |  18 | 张三   |
+----+-----+--------+
1 row in set (0.00 sec)
--3、更新数据,修改id为1的字段的年龄为20
mysql> update account set age=20 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
--4、对事务进行提交
mysql> commit;
Query OK, 0 rows affected (0.04 sec)

使用者:root

--当上方用户执行完第一步时,root同时启动事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
--当上方用户执行完第二步时,root同时进行快照读,读取的结果一样
mysql> select* from account;
+----+-----+--------+
| id | age | name   |
+----+-----+--------+
|  1 |  18 | 张三   |
+----+-----+--------+
1 row in set (0.01 sec)
--当上方用户执行完第三步时,root进行快照读,发现年龄的修改并没有被读到
mysql> select* from account;
+----+-----+--------+
| id | age | name   |
+----+-----+--------+
|  1 |  18 | 张三   |
+----+-----+--------+
1 row in set (0.00 sec)
--当上方用户执行完第四步提交事务时,root再次进行快照读,发现年龄的修改还是没有被读到
mysql> select* from account;
+----+-----+--------+
| id | age | name   |
+----+-----+--------+
|  1 |  18 | 张三   |
+----+-----+--------+
1 row in set (0.00 sec)
--但是此时root使用当前读,使能够读到年龄的修改的
mysql> select* from account lock in share mode;
+----+-----+--------+
| id | age | name   |
+----+-----+--------+
|  1 |  20 | 张三   |
+----+-----+--------+
1 row in set (0.01 sec)

例二:root在jly修改后快照读

使用者:jly

--1、启动事务
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
--2、进行快照读
mysql> select* from account;
+----+-----+--------+
| id | age | name   |
+----+-----+--------+
|  1 |  20 | 张三   |
+----+-----+--------+
1 row in set (0.00 sec)
--3、更新数据,修改id为1的字段的年龄为30
mysql> update account set age=30 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
--4、提交事务
mysql> commit;
Query OK, 0 rows affected (0.03 sec)

使用者:root

--1、同时启动事务
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
--当上方用户执行完第四步提交事务时,root进行快照读,发现读到的数据是被修改过的
mysql> select* from account;
+----+-----+--------+
| id | age | name   |
+----+-----+--------+
|  1 |  30 | 张三   |
+----+-----+--------+
1 row in set (0.00 sec)

通过例一可以发现:root的select在jly提交之前,读到的是修改前的数据;

通过例二可以发现:root的select在jly提交之后,读到的是修改后的数据。

这是因为一个事务在读取时,MySQL会生成一个read view对象,上面介绍read view的章节说了,read view本质就是一个类,用来判断哪些快照我能看到,那些快照我看不到。

read view生成的时机不同,会影响事务的可见性。

5.2MySQL对四种隔离级别的不同处理方式 

Read View生成时机的不同,从而造成RC,RR不同隔离级别下快照读的结果的不同:

可重复读:在RR级别下的某个事务的对某条记录的第一次快照读会创建一个快照及Read View对象, 将当前系统活跃的其他事务记录起来;后续再次调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可见;即RR级别下,快照读生成Read View时,Read View会记录此时所有其他活动事务的快照,这些事务的修改对于当前事务都是不可见的。而早于Read View创建的事务所做的修改才能看见。

读提交:RC级别的事务中,每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下可以看到其他事务所更新内容的原因。正是RC每次快照读,都会形成Read View,所以,RC才会有不可重复读问题。

读未提交:当前读。没有隔离性。

串行化:当前读,对增删改进行加锁的同时,对select也加锁。

三、写写场景

直接理解成当前读。

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

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

相关文章

AI制图工具丨Midjourney产品功能介绍

了解如何使用Discord上的Midjourney Bot通过简单的文本提示创建自定义图像 Midjourney是一款AI制图工具&#xff0c;只要关键字&#xff0c;就能透过AI算法生成相对应的图片&#xff0c;只需要不到一分钟。 可以选择不同画家的艺术风格&#xff0c;例如安迪华荷、达芬奇、达利…

H3C-Cloud Lab实验-OSPF配置实验

一、实验拓扑图 实验需求&#xff1a; 1、按照图示配置 IP 地址 2、按照图示分区域配置 OSPF &#xff0c;实现全网互通 3、为了路由结构稳定&#xff0c;要求路由器使用环回口作为 Router-id&#xff0c;ABR 的环回口宣告进骨干区域 4、掌握OSPF初始化流程、路由表学习的过…

react和vue2/3父子组件的双向绑定

目录 Vue .sync&#xff08;2.3.0&#xff09; $emit &#xff08;2.3后&#xff09; 自定义组件的 v-model 2.2.0 v-modelemits(3.0取消了.sync) React 父组件回调函数 相关基础 框架 MVC &#xff08;Model View Controller&#xff09;/MVP&#xff08;Model View…

西安丨高时空分辨率、高精度一体化预测技术之风、光、水能源自动化预测技术应用

目录 ​第一章 预测平台讲解及安装 第二章 一体化预测工具详解与数据获取及制备 第三章 风资源预测自动化技术 第四章 太阳能资源自动化预测技术 第五章 水资源自动化预测技术 第六章 后处理自动化技术 更多推荐 能源是国民经济发展和人民生活必须的重要物质基础。在过去…

大数据存储架构详解:数据仓库、数据集市、数据湖、数据网格、湖仓一体

前言 本文隶属于专栏《大数据理论体系》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见大数据理论体系 思维导图 数据仓库 数据仓库是一个面向主题的&…

Python自动化之pytest常用插件

目录 1、失败重跑 pytest-rerunfailures 2、多重校验 pytest-assume 3、设定执行顺序 pytest-ordering 4、用例依赖&#xff08;pytest-dependency&#xff09; 5.分布式测试(pytest-xdist) 6.生成报告&#xff08;pytest-html&#xff09; 1、失败重跑 pytest-rerunfailu…

华夏ERP在虚拟机Ubuntu上的安装(测试实例)

1.虚拟机软件VirtualBOX 7.0 2.Ubuntu 版本 3.宝塔面板安装 百度搜索宝塔面板&#xff0c;按官网提示进行安装。下面截图是官网示例。 if [ -f /usr/bin/curl ];then curl -sSO download.cnnbt.net/install_panel.sh;else wget -O install_panel.sh download.cnnbt.net/install…

EasyCVR告警类型设置后首页需要刷新才能更新的问题优化

EasyCVR视频融合平台基于云边端一体化架构&#xff0c;可支持多协议、多类型设备接入&#xff0c;包括&#xff1a;NVR、IPC、视频编码器、无人机、车载设备、智能手持终端、移动执法仪等。平台具有强大的数据接入、处理及分发能力&#xff0c;可在复杂的网络环境中&#xff0c…

【C++初阶】构造函数和析构函数

文章目录 一、类的六个默认成员函数二、构造函数三、析构函数 一、类的六个默认成员函数 &#x1f4d6;默认成员函数 用户没有显式实现&#xff0c;编译器会自动生成的成员函数&#xff0c;称为默认成员函数。 构造函数&#xff1a;完成对象的初始化工作。析构函数&#xff…

动态sql语句

1.1 动态sql语句概述 Mybatis 的映射文件中&#xff0c;业务逻辑复杂时&#xff0c; SQL是动态变化的&#xff0c;此时在前面的学习中 SQL 就不能满足要求了。 参考的官方文档&#xff1a; 1.2 动态 SQL 之<if> 根据实体类的不同取值&#xff0c;使用不同的 SQL语句…

园区能源控制管理系统

园区能源控制管理系统是一种能够实现对园区内能源消耗、供应和分配进行实时监控、管理和控制的系统。该系统通过对园区内各种能源设备的数据采集、处理和分析&#xff0c;为管理者提供实时的能源使用情况和数据分析&#xff0c;从而帮助管理者制定科学的能源管理策略和节能措施…

3Ds max图文教程:高精度篮球3D建模

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 第 1 步。使用以下设置在顶部视口上创建球体&#xff1a; 第 2 步。将球体转换为可编辑的多边形&#xff1a; 第 3 步。转到 Edge 子对象级别并剪切以下边缘&#xff1a; 第 4 步。选择以下边&#xff0c;然…

github 最简单的使用步骤(个人学习记录~)

github 使用步骤&#xff1a; (11条消息) github新手用法详解&#xff08;建议收藏&#xff01;&#xff01;&#xff01;&#xff09;_github详解_怪 咖的博客-CSDN博客 1.获取ssh密钥 打开输入&#xff1a;ssh-keygen -t rsa -C “git账号” 输入之后一路Enter&#xff08…

Kubernetes Service的过程

文章目录 Kubernetes Service的实现基础内容1. 命令 ip route show table all2. DNAT3. IPVS和iptables4. Service Service的实现简述 Kubernetes Service的实现 基础内容 在了解Service之前,需要先了解一些额外的知识: 命令: ip route show table allDNATIPVS和iptables基础…

【云原生】K8S单节点搭建

Kubernetes Kubernetes基础概念架构1、基础环境2、安装kubelet、kubeadm、kubectl 2、使用kubeadm引导集群1、下载各个机器需要的镜像2、初始化主节点 Kubernetes核心实战Pod Kubernetes基础概念 kubernetes具有以下特性&#xff1a; ● 服务发现和负载均衡 Kubernetes 可以使…

EasyCVR录像阈值配置未生效,是什么原因?

有用户反馈&#xff0c;在平台中设置了录像阈值不生效&#xff0c;导致磁盘爆满。针对该反馈&#xff0c;我们立即进行了排查。 EasyCVR基于云边端一体化架构&#xff0c;可支持多协议、多类型设备接入&#xff0c;在视频能力上&#xff0c;平台可实现视频直播、录像、回放、检…

小白带你学习Linux的rsync的基本操作(二十四)

目录 前言 一、概述 二、特性 1、快速 2、安全 三、应用场景 四、数据的同步方式 五、rsync传输模式 六、rsync应用 七、rsync命令 1、格式 2、选项 3、举例 4、配置文件 5、练习 八、rsyncinotfy实时同步 1、服务器端 2、开发客户端 前言 Rsync是一个开源的…

分布式应用之zookeeper集群+消息队列Kafka

一、zookeeper集群的相关知识 1.zookeeper的概念 ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能…

【深入探究人工智能】:历史、应用、技术与未来

深入探究人工智能 前言人工智能的历史人工智能的应用人工智能的技术人工智能的未来当代的人工智能产物结语&#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &am…

软件渗透测试真的很重要吗?渗透测试有哪些测试流程?

软件渗透测试是指通过模拟恶意攻击者的行为&#xff0c;评估软件系统中的潜在安全漏洞和弱点的活动。这种安全测试方法能够帮助开发人员和系统管理员发现并修复潜在的安全漏洞&#xff0c;以确保软件系统的安全性和完整性。软件渗透测试是一项高度技术性的任务&#xff0c;需要…