MySQL MVCC的详解之Read View

文章目录

    • 概要
    • 一、基于UNDO LOG的版本链
        • 1.1、行记录结构
        • 1.2、了解UNDO LOG
        • 1.3、版本链
    • 二、Read View
        • 2.1、判定机制
    • 三、参考

概要

在上文中,我们提到了MVCC(Multi-Version Concurrency Control)多版本并发控制,是通过undo log来实现的。那具体是如何实现的呢?将在本文一一道来。

MVCC是为了实现非阻塞读,即提高数据库并发读能力的一种机制。

通常来说,A事务正在修改数据行X,在修改未结束前,B事务要读数据行X,为了避免读到脏数据,B就会被阻塞,直到A事务修改完数据行X,MVCC很好的避免了这种情况的发生。

MVCC是通过保存数据在某个时间点的快照来实现的,即保存一个数据行的多个变更版本(空间换时间)。这些版本就是undo log了,每一行的变更记录就存在undo log中,通过链表联系在一起,构成了一个完整的版本链,供MVCC实现非阻塞读。

例如在可重复读隔离级别下,A事务正在修改数据行X,在修改数据行X前会把其当前记录插入到版本链中,B事务要读数据行X就到版本链中找符合的,这样B就不会被阻塞了。

ps:MySQL的MVCC只作用于在REPEATABLE READ和READ COMMITED两个隔离级别下执行普通的SELECT操作。

在高性能MySQL第三版一书中对MVCC的操作描述如下:
高性能MySQL第三版一书中对MVCC的操作描述下面来一起探索下具体实现吧。

本文背景:MySQL InnoDB存储引擎。

一、基于UNDO LOG的版本链

在了解版本链之前,首先看一下InnoDB存储引擎的行记录。

1.1、行记录结构

提到MySQL的行记录,肯定会想到行ID、用户数据列等内容,除了这些信息外,还有一些隐藏信息,比如事务ID、回滚指针等其他额外信息,那我们可以得出下图:
MySQL行记录结构简单示意图
其中事务ID(trx_id)、回滚指针(rollback_ptr)是本文要讲的核心。

ps:InnoDB的行记录是存储在聚族索引中的

1.2、了解UNDO LOG

                                            MySQL undo log结构示意图

MySQL undo log结构示意图

MySQL的undo log分为两大类:

  1. insert undo:insert 操作产生的,记录了table_id、trx_id、主键各列数据等信息。
  2. update undo:update和delete操作产生的,

虽说update和delete操作产生的undo log都会记录到update undo这个大类,但其记录内容是有很大差距的。
delete操作产生的undo log会记录table_id、trx_id、rollback_ptr、主键各列数据等信息,而update操作产生的undo log会记录更新table_id、trx_id、rollback_ptr、被更新列旧值、主键各列数据等信息。

ps:可以看到undo log中并没有记录用户列数据

1.3、版本链

我们现在在test库下有一个test表:
MySQL 测试表下面我们经过一系列插入,删除,更新来演示版本链的变迁:
假设当前全局trx_id = 101。

  1. 插入一条数据
insert into test (id,num,name,key_id) values (1,1,"bob",11);

此时有:
插入一条数据

  1. 更新数据
    1)令id=1的数据name=tom
update test set name = "tom" where id = 1;

此时有:
更新一条数据
2)令id=1的数据name=join

update test set name = "join" where id = 1;

此时有:
更新一条数据

  1. 删除数据,删除id=1的数据
delete from test where id = 1;

此时有:
删除一条数据
如上图,在对id=1这一条记录的插入,更新,删除的过程中构建了一个版本链。其中删除操作,只是在聚簇索引上的记录中打了删除标记,并不会立即删除,而是当没有Read View持有该事务ID时才会有purge线程去真的去删除,之后这块空间才能被使用,为什么不能立即删除呢,主要是因为undo log中并不保存所有的用户列数据,甚至不保存,都是基于聚簇索引中的记录行,在结合undo log内容,在回滚过程中构造某个版本的数据。

另外,我们在1.2小节中强调了 undo log中并不记录用户列数据,这里只是为了表示方便才画了出来,其实MySQL是基于聚簇索引上的记录内容,通过从聚簇索引上的记录roll_ptr开始,依次回滚,直到遇到符合要求的事务ID,构造出最终数据。

假设第二次更新操作开始但未提交时,有个trx_id=107的事务要读id=1的数据,此时发现ID=1的数据trx_id=106且处于活跃状态,则需要沿着版本链回滚,当遇到trx_id=102的记录,结合聚簇索引上的记录和trx_id=102的undo记录,构造出trx_id=102的完整数据,对于trx_id=107的事务来说,该事务是已提交的事务,则读取即可。其实这就是Read View了,详情请看下一章节。

二、Read View

对于READ COMMITED隔离级别,需要读已经提交的数据,那当A事务修改完聚簇索引上的记录X后尚未提交,此时B事务读取记录X,按照定义,此时聚簇索引上的记录是不允许读取的,如何判定呢?就依赖Read View机制了

对于REPEATABLE READ隔离级别,需要重复读数据,那当A事务读取记录X后,B事务修改完聚簇索引上的记录X并提交,此时A事务需要在此读取记录X,按照定义,此时聚簇索引上的记录是不允许读取的,如何判定呢?也就依赖Read View机制了

Read View也称作一致性视图,其主要包含4个主要的内容:

  • m_ids:在生成Read View时,当前系统中活跃的读写事务的事务ID列表;
  • min_trx_id:在生成Read View时,当前系统中活跃的读写事务中最小的事务ID,即m_ids中的最小值;
  • max_trx_id:在生成Read View时,当前系统中活跃的读写事务中最大的事务ID,即系统应该分配给下一个事务的事务ID(全局事务ID的值);
  • creator_trx_id:在生成Read View时,当前事务的事务ID。

其中max_trx_id要注意下,并不一定是m_ids中的最大值,而是生成Read View时的全局事务ID值。因为事务ID时递增(循环)分配的,在RR隔离级别下,假设当前活跃的事务ID有1,2,3,事务ID=3的事务提交后,再开启一个事务A,发起读操作,此时Read View时m_ids=[1,2,4],min_trx_id=1,max_trx_id=4,如果有另一个写事务B提交了,消耗了一个事务ID=4,那么此时事务A进行写操作,就会出现creator_trx_id=5的情况。

为什么会这样呢?我们要明白事务ID的生成并不是开启事务(执行begin操作)时就确定的,而是第一次执行写操作时确定的
而Read View生成时机是在读操作前确定的,但RC与RR还不同,RC是每次读操作前都生成一个Read View(保证可以读已提交数据),而RR是在第一次读操作前生成一个Read View,就不会变动了(保证可重复读)

2.1、判定机制

MySQL根据Read View读要访问的记录依次进行以下判定,来决定是否可访问:

  1. 如果被访问记录的trx_id等于creator_trx_id相等,这意味当前事务在访问它自己修改的记录,允许被访问;
  2. 如果被访问记录的trx_id小于min_trx_id,这意味被访问记录在当前事务生成Read View时已经提交了,允许被访问;
  3. 如果被访问记录的trx_id大于等于max_trx_id,这意味被访问记录在当前事务生成Read View之后产生的,不允许被访问;
  4. 如果被访问记录的trx_id在m_ids中,说明在当前事务生成Read View时被访问记录所属的事务还是活跃的,不允许被访问;
  5. 如果被访问记录的trx_id不在m_ids中,说明在当前事务生成Read View时被访问记录所属的事务已经提交了,允许被访问;

以上的判定机制,是实现RC和RR的基础。

select trx_id,trx_state,trx_started,trx_rows_locked from INFORMATION_SCHEMA.INNODB_TRX; #可以查看当前活跃的事务id等信息

针对1.3小节的版本链案例:
我们依次执行下面四个语句,看看效果如何(RR隔离级别):
1:

insert into test (id,num,name,key_id) values (1,1,"bob",11);#事务id = 101

2:

begin;
update test set name = "tom" where id = 1; #事务id = 102

3:

insert into test (id,num,name,key_id) values (2,2,"2ob",22);#事务id = 103
insert into test (id,num,name,key_id) values (3,3,"3ob",33);#事务id = 104

4:

begin;                    
update test set name = "uuu" where id = 2; #事务id = 105

5:

begin;
select * from test where id = 1;
update test set name = "uuu" where id = 3; #事务id = 106

那么第五句select * from test where id = 1;的Read View如下:
m_ids=[102,105],min_trx_id=102,max_trx_id=106, creator_trx_id = 0

针对id=1这条记录,此时test表聚簇索引中的记录是 (1,1,"tom",11),隐藏字段trx_id=102。但是102在m_ids中,所以不可见,根据其undo log得到最终结果 (1,1,"bob",11)

三、参考

1]:庖丁解InnoDB之Undo LOG
2]:正确的理解MySQL的MVCC及实现原理

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

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

相关文章

低代码系列——初步认识低代码

低代码系列目录 一、初步认识低代码 二、低代码是什么 三、低代码平台的概念和分类 01.无代码开发平台 02.低代码应用平台(LCAP) 03.多重体验开发平台(MXDP) 04.智能业务流程管理套件(iBPMS) 四、低代码的能力指标 五、低代码平台jnpf 表单 报表 流程 权限 一、初步认识低代码 …

隧道HTTP具备的条件

作为一名专业的爬虫代理供应商,我们都知道使用代理是保证爬虫的高效性和稳定性的重要手段之一。而隧道代理则是近年来备受推崇的一种代理形式,它通过将请求通过隧道传输,可以有效地隐藏爬虫的真实IP地址,提高爬虫的反爬能力。 在…

Maven 一键部署到 SSH 服务器

简介 利用 Maven Mojo 功能一键部署 jar 包或 war 包到远程服务器上。 配置 在 maven 的setting.xml 配置服务器 SSH 账号密码。虽然可以在工程的 pom.xml 直接配置&#xff0c;但那样不太安全。 <servers><server><id>iq</id><configuration&…

完全免费的GPT,最新整理,2023年8月24日,已人工验证,不用注册,不用登录,更不用魔法,点开就能用

完全免费的ChatGPT&#xff0c;最新整理&#xff0c;2023年8月24日&#xff0c;已人工验证&#xff0c; 不用注册&#xff0c;不用登录&#xff0c;更不用魔法&#xff0c;点开就能用&#xff01; 第一个&#xff1a;网址地址统一放在文末啦&#xff01;文末直达 看上图你就能…

无涯教程-PHP - Session选项

从PHP7 起&#xff0c; session_start()()函数接受一系列选项&#xff0c;以覆盖在 php.ini 中设置的会话配置指令。这些选项支持 session.lazy_write &#xff0c;默认情况下此函数为on&#xff0c;如果会话数据已更改&#xff0c;则会导致PHP覆盖任何会话文件。 添加的另一个…

[JavaWeb]【八】web后端开发-Mybatis

目录 一 介绍 二 Mybatis的入门 2.1 快速入门 2.1.1 准备SpringBoot工程 2.1.2 创建数据库mybatis以及对应库表user 2.1.3 创建User实体类 2.1.4 配置application.properties数据库连接信息 2.1.5 编写sql语句&#xff08;注解方式&#xff09; 2.1.6 测试运行 2.1.7 配…

通过浏览器控制台使用js脚本进行浏览器操作(定时点击等)

进行此操作前我们首先需要了解js编程语言 --了解之后我们就可以去操作了 这里我们拿csdn评论举例子&#xff1b; 点开评论界面右键审查元素 此时我们需要找到输入框dom和评论按钮dom 点击元素之后点击箭头然后去界面上选中文本框核按钮 然后我们就可以知道这个文本框的id 同…

POI groupRow 折叠分组,折叠部分不显示问题

折叠组是什么&#xff1f;如图就是用POI 实现的&#xff0c;代码很简单&#xff1a;sheet.groupRow(开始行&#xff0c;结束行)即可 但是万万没想到&#xff0c;最终实现出的结果&#xff0c;合并的组&#xff0c;有一部分并没有渲染出来&#xff0c;如下图&#xff1a; 因为我…

分布式下的session共享问题

首页我们确定在分布式的情况下session是不能共享的。 1.不同的服务&#xff0c;session不能共享&#xff0c;也就是微服务的情况下 2.同一服务在分布式情况&#xff0c;session同样不能共享&#xff0c;也会是分布式情况 分布式下session共享问题解决方案(域名相同) 1.session复…

【leetcode 力扣刷题】交换链表中的节点

24. 两两交换链表中的节点 24. 两两交换链表中的节点两两节点分组&#xff0c;反转两个节点连接递归求解 24. 两两交换链表中的节点 题目链接&#xff1a;24. 两两交换链表中的节点 题目内容&#xff1a; 题目中强调不能修改节点内部值&#xff0c;是因为如果不加这个限制的话…

java.lang.IllegalStateException: Failed to load ApplicationContext异常

错误原由 今天在编写SpringBoot项目整合MyBatis框架的过程中&#xff0c;产生了这个错误 java.lang.IllegalStateException: Failed to load ApplicationContextat org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheA…

Linux和其他类Unix系统的GNU coreutils 知多少

GNU coreutils是GNU项目的一部分&#xff0c;它是一组基本的命令行工具集&#xff0c;用于操作和管理Linux和其他类Unix系统中的文件和数据流。这些工具被广泛用于终端和脚本中&#xff0c;提供了许多常用的功能和实用程序。Linux和其他类Unix系统的GNU coreutils 知多少&#…

用 VB.net,VBA 两种方式 读取单元格内的 换行数据,并出力到 CSV文件

用 VB.net&#xff0c;VBA 两种方式 读取单元格内的 换行数据&#xff0c;并出力到 CSV文件 需求 如下图所示&#xff0c;为了生成csv文件导入数据库&#xff0c;需要将下图 的 1 和 2 拼接成 如下 3 所示的一行数据&#xff0c; 开头为 1 &#xff0c;往后为 2 的换行数据 将换…

HTML中SCRIPT 标签中的那些属性

在HTML中&#xff0c; <script> 标签用于嵌入或引用JavaScript代码。 在 <script> 标签中&#xff0c;有两个属性可以用来控制脚本的加载和执行方式&#xff1a; async 和 defer 。 当然这也是常见的一道面试题&#xff0c; async 和 defer 的作用和区别。 asy…

[Go版]算法通关村第十三关黄金——数字数学问题之数论问题(最大公约数、素数、埃氏筛、丑数)

目录 题目&#xff1a;辗转相除法&#xff08;求最大公约数&#xff09;思路分析&#xff1a;辗转相除法&#xff08;也叫欧几里得算法&#xff09;gcd(a,b) gcd(b,a mod b)复杂度&#xff1a;时间复杂度 O ( n l o g ( m a x ) ) O(nlog(max)) O(nlog(max))、空间复杂度 O (…

轻松实现文件夹名互换,快速批量改名高手工具助您高效管理!

亲爱的用户们&#xff0c;您是否曾经需要将文件夹的名称进行互换&#xff0c;但手动一个一个改名太过繁琐&#xff1f;现在&#xff0c;我们为您推出一款高效的文件夹批量改名工具&#xff0c;让您轻松实现文件夹名的互换&#xff0c;帮助您更好地管理文件&#xff01; 首先&a…

python中的matplotlib画散点图(数据分析与可视化)

python中的matplotlib画散点图&#xff08;数据分析与可视化&#xff09; import numpy as np import pandas as pd import matplotlib.pyplot as pltpd.set_option("max_columns",None) plt.rcParams[font.sans-serif][SimHei] plt.rcParams[axes.unicode_minus]Fa…

苹果iPhone 15 Ultra和iPhone 15 Pro Max:新名字是否值得期待?

我们即将发现一个名字里有什么,至少如果一个关于iPhone 15 Pro Max的新谣言被证明是准确的。一份新的报告表明,当这款手机可能在苹果9月的发布会上首次亮相时,苹果可能会放弃Pro Max的名字,而将其称为iPhone 15 Ultra。 改名的原因是什么?好吧,这肯定会将苹果最高端的手…

PHP实现每日蛋白质摄入量计算器

1.laravel 路由 //每日蛋白质摄入计算器Route::get(api/protein/intake, FormulaControllerproteinIntakeCal); 2.代码 /*** 每日蛋白质摄入计算器*/public function proteinIntakeCal(){$number intval($this->request(number));$goalFactor array(0.8, 1.16, 0.8, 1.16,…

【sql】MongoDB 增删改查 高级用法

【sql】MongoDB 增删改查 高级用法 相关使用文档 MongoDB Query API — MongoDB Manual https://www.mongodb.com/docs/manual/reference/sql-comparison //增 //新增数据2种方式 db.msg.save({"name":"springboot&#x1f600;"}); db.msg.insert({&qu…