深入理解MVCC与Read View:并发控制的关键要素

MVCC

  • MVCC的几个问题
    • 1.update、insert、select和delete如何在MVCC中维护版本链?
    • 2.select读取,是读取最新的版本呢?还是读取历史版本?
    • 3.当前读和快照读
    • 4.那为什么要有隔离级别呢?
    • 5.如何保证,不同的事务,看到不同的内容呢?也就是如何如何实现隔离级别?
      • Read View
        • 一、Read View的作用
        • 二、Read View的创建
        • 三、Read View的使用
        • Read view结构体理解
        • 整体流程
  • 再谈R-R下的快照读与R-C下的快照读
    • RR 与 RC的本质区别

MVCC的几个问题

1.update、insert、select和delete如何在MVCC中维护版本链?

  • 通过上述可知(upadte)通过MVCC维护。
  • delete也是一样的,别忘了,删数据不是清空,而是设置flag为删除即可。也可以形成版本。
  • select不会对数据做任何修改,所以,为select维护多版本,没有意义。
  • 因为insert是插入,也就是之前没有数据,那么insert也就没有历史版本。但是一般为了回滚操作,insert的数据也是要被放入undo log中,如果当前事务commit了,那么这个undo log 的历史insert记录就可以被清空了。

总结一下,也就是我们可以理解成,updatedelete可以形成版本链,insert暂时不考虑。

2.select读取,是读取最新的版本呢?还是读取历史版本?

默认状态R-R
在这里插入图片描述

我们来看看下面两张图
在这里插入图片描述
在这里插入图片描述

可以看出如果在更新操作前其他事务未查询,提交后再进行查询,则读取到的为最新更改数据;而如果在提交前进行了查询操作,为了不引起幻读,保持了事务内和第一次查询一样的结果

那么我们怎么在事务内获取最新数据呢?

select date from table lock in share mode(共享锁)

在这里插入图片描述

3.当前读和快照读

  • 当前读:读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。增删改,都叫做当前读,select也有可能当前读,比如:select lock in share mode(共享锁), select for update

  • 快照读:读取历史版本(一般而言),就叫做快照读。

4.那为什么要有隔离级别呢?

事务都是原子的。所以,无论如何,事务总有先有后。

但是经过上面的操作我们发现,事务从begin->CURD->commit,是有一个阶段的。也就是事务有执行前,执行中,执行后的阶段。但,不管怎么启动多个事务,总是有先有后的。

那么多个事务在执行中,CURD操作是会交织在一起的。那么,为了保证事务的“有先有后”,是不是应该让不同的事务看到它该看到的内容,这就是所谓的隔离性与隔离级别要解决的问题。

5.如何保证,不同的事务,看到不同的内容呢?也就是如何如何实现隔离级别?

Read View

Read View就是事务进行 快照读 操作的时候生产的 读视图 (Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)
Read View 在 MySQL 源码中,就是一个类,本质是用来进行可见性判断的。 即当我们某个事务执行快照读的时候,对该记录创建一个 Read View 读视图,把它比作条件,用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的 undo log 里面的某个版本的数据。
从内核源码解析数据库中的Read View

在数据库管理系统中,特别是那些支持多版本并发控制(MVCC)的系统中,Read View(读视图)是一个非常重要的概念。它决定了事务在读取数据时能看到哪些版本的数据。下面,我们将从内核源码的角度来深入解析Read View的实现和工作原理。

一、Read View的作用

在MVCC中,每个事务都有一个Read View,它代表了事务开始时刻的数据库快照。通过这个Read View,事务可以安全地读取数据,而不用担心其他事务的并发修改。Read View的作用是确保事务的隔离性,防止脏读、不可重复读和幻读等问题。

二、Read View的创建

在源码中,Read View的创建通常是在事务开始时进行的。这个过程会涉及到以下几个关键步骤:

  1. 获取当前活跃事务列表:遍历事务管理系统,找出当前所有活跃(即未提交)的事务,并将它们的事务ID加入到活跃事务列表中。

  2. 确定min_trx_id和max_trx_id:min_trx_id是活跃事务列表中的最小事务ID,而max_trx_id是下一个待分配的事务ID(通常是当前最大事务ID加1)。这两个值将用于后续判断数据版本的可见性。

  3. 创建Read View结构体:根据获取到的活跃事务列表、min_trx_id和max_trx_id等信息,创建一个Read View结构体。这个结构体通常包含了一些关键的成员变量,如事务ID列表、min_trx_id、max_trx_id等。

三、Read View的使用

在事务执行过程中,每次读取数据时都会使用到Read View。具体来说,读取数据的过程会按照以下步骤进行:

  1. 查找数据版本:根据主键或索引找到相应的数据行,并获取该数据行的所有版本信息(包括版本号和对应的事务ID)。

  2. 判断数据版本的可见性:遍历数据版本链表,对每个版本进行判断。如果版本的事务ID小于min_trx_id,说明这个版本在事务开始之前就已经提交了,因此是可见的;如果版本的事务ID等于当前事务的ID,说明这个版本是当前事务自己修改的,因此也是可见的;如果版本的事务ID在活跃事务列表中,说明这个版本是由其他未提交的事务修改的,因此是不可见的;如果版本的事务ID大于max_trx_id,说明这个版本是在当前事务开始后创建的,因此也是不可见的。

  3. 选择可见的数据版本:根据上一步的判断结果,选择第一个可见的数据版本作为读取结果。如果找不到可见的版本(即所有数据版本都不可见),则返回空或抛出异常。

Read view结构体理解

在这里插入图片描述
简略下来就是这几个主要字段
在这里插入图片描述
我们在实际读取数据版本链的时候,是能读取到每一个版本对应的事务ID的,即:当前记录的 DB_TRX_ID。那么,我们现在手里面有的东西就有,当前快照读的 ReadView 和 版本链中的某一个记录的 DB_TRX_ID。所以现在的问题就是,当前快照读,应不应该读到当前版本记录。
在这里插入图片描述
通过上图我们可以知道:

  1. 事务ID的大小与可见性

    • 一般来说,一个具有较大事务ID的事务能够“看到”具有较小事务ID的事务所做的更改(如果它们没有被其他后续事务覆盖或删除)。
    • 一个具有较小事务ID的事务则不能“看到”具有较大事务ID的事务所做的更改,直到它重新获取一个新的、更大的事务ID(即,当该事务再次开始一个新的事务时)。
  2. Read View

    • 在MVCC中,当事务想要读取一行数据时,它并不是直接读取该行的最新版本,而是基于当前事务的ID(或时间戳)和一个“read view”来确定应该读取哪个版本的数据。
    • Read view通常包含了在当前事务开始时仍然活跃(即未提交或未回滚)的所有其他事务的ID。这是为了确保当前事务不会读取到任何可能在未来被这些活跃事务回滚的数据版本。
  3. 注意

    • 并不是所有具有较小ID的事务都不能看到具有较大ID的事务的更改。这取决于事务何时开始以及何时读取数据。
    • 如果一个具有较小ID的事务在具有较大ID的事务提交之后开始,并且没有其他活跃事务干扰,那么它仍然可以看到那个具有较大ID的事务的更改。
    • 此外,某些数据库可能允许事务使用特定的查询选项(如“脏读”、“不可重复读”或“幻读”)来故意读取未提交的数据或看到其他事务的更改,但这超出了标准MVCC行为的范围。

快照到事务ID不一定是连续的理解——虽然事务ID一直是自增的,但是有的事务持续时间长,有的事务时间短;晚来的事务可能先完成提交,先来的事务可能没完成而在快照时并未提交,所以快照所得的事务ID只有未完成的,也就是在版本链中的,已经提交的历史事务一定是能被看到的,所以可能产生快照到的事务ID不连续。

对应源码为在这里插入图片描述

如果查到不应该看到当前版本,接下来就是遍历下一个版本,直到符合条件,即可以看到。上面的 readview 是当你进行select的时候,会自动形成。

整体流程

假设当前有条记录:在这里插入图片描述
事务进行操作
在这里插入图片描述

  • 事务4:修改name(张三) 变成name(李四)
  • 当 事务2 对某行数据执行了 快照读 ,数据库为该行数据生成一个 Read View 读视图
//事务2的 Read View m_ids; // 1,3 
up_limit_id; // 1 
low_limit_id; // 4 + 1 = 5,原因:ReadView生成时刻,系统尚未分配的下一个事务ID 
creator_trx_id // 2 

此时对应的版本链为
在这里插入图片描述
只有事务4修改过该行记录,并在事务2执行快照读前,就提交了事务。在这里插入图片描述
我们的事务2在快照读该行记录的时候,就会拿该行记录的 DB_TRX_ID 去跟 up_limit_id,low_limit_id和活
跃事务ID列表(trx_list) 进行比较,判断当前事务2能看到该记录的版本。

//事务2的 Read View 
m_ids; // 1,3 
up_limit_id; // 1 
low_limit_id; // 4 + 1 = 5,原因:ReadView生成时刻,系统尚未分配的下一个事务ID 
creator_trx_id // 2 
  • //事务4提交的记录对应的事务ID
    DB_TRX_ID=4

  • //比较步骤
    DB_TRX_ID(4)< up_limit_id(1) ? 不小于,下一步
    DB_TRX_ID(4)>= low_limit_id(5) ? 不大于,下一步
    m_ids.contains(DB_TRX_ID) ? 不包含,说明,事务4不在当前的活跃事务中。

故,事务4的更改,应该看到。 所以事务2能读到的最新数据记录是事务4所提交的版本,而事务4提交的版本也是全局角度上最新的版本

再谈R-R下的快照读与R-C下的快照读

当前读和快照读在RR级别下的区别
流程表如图:
在这里插入图片描述
在这里插入图片描述

  • 用例1与用例2:唯一区别仅仅是 表1 的事务B在事务A修改age前 快照读 过一次age数据
  • 而 表2 的事务B在事务A修改age前没有进行过快照读。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

事务中快照读的结果是非常依赖该事务首次出现快照读的地方,即某个事务中首次出现快照读,决定该事务后 续快照读结果的能力 delete同样如此

而对于R-C相同的操作
在这里插入图片描述
在这里插入图片描述

RR 与 RC的本质区别

  • 正是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;而在RR隔离级别下,则是同一个事务
    中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。
    正是RC每次快照读,都会形成Read View,所以,RC才会有不可重复读问题。

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

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

相关文章

Automa:一键自动化,网页数据采集与工作流程优化专家

Automa&#xff1a;解锁自动化浏览器潜能&#xff0c;赋能工作效率&#xff0c;让复杂任务变得简单- 精选真开源&#xff0c;释放新价值。 概览 Automa是一款创新的网页自动化工具&#xff0c;专为寻求提升工作效率、简化数据收集过程的现代工作者设计。它融合了先进的数据抓取…

模板:vector(顺序表容器)

1.构造函数 explicit vector (const allocator_type& alloc allocator_type()); //默认构造函数explicit vector (size_type n, const value_type& val value_type(),const allocator_type& alloc allocator_type()); //n个重复的valtemplate <class Input…

Angular入门

Angular版本&#xff1a;Angular 版本演进史概述-天翼云开发者社区 - 天翼云 安装nodejs&#xff1a;Node.js安装与配置环境 v20.13.1(LTS)-CSDN博客 Angular CLI是啥 Angular CLI 是一个命令行接口(Angular Command Line Interface)&#xff0c;是开发 Angular 应用的最快、最…

大模型时代下两种few shot高效文本分类方法

介绍近年(2022、2024)大语言模型盛行下的两篇文本分类相关的论文&#xff0c;适用场景为few shot。两种方法分别是setfit和fastfit&#xff0c;都提供了python的包使用方便。 论文1&#xff1a;Efficient Few-Shot Learning Without Prompts 题目&#xff1a;无需提示的高效少…

深入了解 MyBatis 插件:定制化你的持久层框架

序言 MyBatis 是一个流行的 Java 持久层框架&#xff0c;它提供了简单而强大的数据库访问功能。然而&#xff0c;有时候我们需要在 MyBatis 中添加一些自定义的功能或行为&#xff0c;来满足特定的需求。这时&#xff0c;MyBatis 插件就发挥了重要作用。本文将深入探讨 MyBati…

An 2024下载

An2024下载&#xff1a; 百度网盘下载https://pan.baidu.com/s/1cQQCFL16OUY1G6uQWgDbSg?pwdSIMS Adobe Animate 2024&#xff0c;作为Flash技术的进化顶点&#xff0c;是Adobe匠心打造的动画与交互内容创作的旗舰软件。这款工具赋予设计师与开发者前所未有的创意自由&#x…

HIVE卡口流量需求分析

HIVE卡口流量需求分析 目录 HIVE卡口流量需求分析 1.创建表格 插入数据 2.需求 3.总结&#xff1a; 1.创建表格 插入数据 CREATE TABLE learn3.veh_pass( id STRING COMMENT "卡口编号", pass_time STRING COMMENT "进过时间", pass_num int COMMENT …

【iOS】架构模式

文章目录 前言一、MVC二、MVP三、MVVM 前言 之前写项目一直用的是MVC架构&#xff0c;现在来学一下MVP与MVVM两种架构&#xff0c;当然还有VIPER架构&#xff0c;如果有时间后面会单独学习 一、MVC MVC架构先前已经详细讲述&#xff0c;这里不再赘述&#xff0c;我们主要讲一…

Golang | Leetcode Golang题解之第87题扰乱字符串

题目&#xff1a; 题解&#xff1a; func isScramble(s1, s2 string) bool {n : len(s1)dp : make([][][]int8, n)for i : range dp {dp[i] make([][]int8, n)for j : range dp[i] {dp[i][j] make([]int8, n1)for k : range dp[i][j] {dp[i][j][k] -1}}}// 第一个字符串从 …

【SAP ABAP学习资料】通过RFC接口上传图片至SAP 图片格式转换 图片大小调整

SAP图片相关&#xff1a; 链接: 【SAP ABAP学习资料】图片上传SAP 链接: 【SAP ABAP学习资料】屏幕图片预览 链接: 【SAP ABAP学习资料】smartforms打印图片&#xff0c;动态打印图片 需求&#xff1a; SAP上传图片只能本地电脑选择图片通过SE78或PERFORM IMPORT_BITMAP_BDS上…

Milvus入门初探

引言 Milvus 是一款开源的向量数据库&#xff0c;专为处理向量搜索任务而设计。它支持多种类型的向量&#xff0c;如浮点向量、二进制向量等&#xff0c;并且可以处理大规模的向量数据。Milvus 在 AI 应用中非常流行&#xff0c;尤其是在需要执行相似性搜索或最近邻搜索的场景…

【超详细】跑通YOLOv8之深度学习环境配置3-YOLOv8安装

环境配置3下载安装内容如下&#xff1a; 1、配置清华等镜像源 2、创建环境 3、下载安装Pytorch 4、下载安装YOLOv8运行环境 版本&#xff1a;Python3.8&#xff08;要求>3.8&#xff09;&#xff0c;torch1.12.0cu113&#xff08;要求>1.8&#xff09; 1、配置清华等镜…

算法-卡尔曼滤波之为什么要使用卡尔曼滤波器

假设使用雷达来预测飞行器的位置&#xff1b; 预先的假设条件条件: 1.激光雷达的激光束每5s发射一次&#xff1b; 2.通过接受的激光束&#xff0c;雷达估计目标当前时刻的位置和速度&#xff1b; 3.同时雷达要预测下一时刻的位置和速度 根据速度&#xff0c;加速度和位移的…

ESP32重要库示例详解(三):按键之avdweb_Switch库

在Arduino开发中&#xff0c;我们经常需要处理按钮和开关的输入。avdweb_Switch库就是为了简化这一任务&#xff0c;提供了一个优雅且高效的事件处理方式。本文将通过一个实际示例&#xff0c;介绍该库的主要特性和用法。 导入库 在Arduino IDE导入avdweb_Switch库的步骤如下…

Python---NumPy万字总结【此篇文章内容难度较大,线性代数模块】(3)

NumPy的应用&#xff08;3&#xff09; 向量 向量&#xff08;vector&#xff09;也叫矢量&#xff0c;是一个同时具有大小和方向&#xff0c;且满足平行四边形法则的几何对象。与向量相对的概念叫标量或数量&#xff0c;标量只有大小&#xff0c;绝大多数情况下没有方向。我们…

家居分类的添加、修改、逻辑删除和批量删除

文章目录 1.逻辑删除家居分类1.将之前的docker数据库换成云数据库2.树形控件增加添加和删除按钮1.找到控件2.粘贴四个属性到<el-tree 属性>3.粘贴两个span到<el-tree>标签里4.代码5.效果6.方法区新增两个方法处理添加和删除分类7.输出查看一下信息8.要求节点等级小…

李开复引领的零一万物开源了Yi-1.5模型,推出了6B、9B、34B三个不同规模的版本

零一万物&#xff0c;由李开复博士引领的AI 2.0公司&#xff0c;近期开源了其备受瞩目的Yi-1.5模型&#xff0c;这一举措再次彰显了公司在人工智能领域的创新实力与开放精神。Yi-1.5模型作为零一万物的重要技术成果&#xff0c;不仅代表了公司在大模型技术研发上的新高度&#…

冥想的时候怎么专注自己

冥想的时候怎么专注自己&#xff1f;我国传统的打坐养生功法&#xff0c;实际最早可追溯到五千年前的黄帝时代。   每天投资两个半小时的打坐&#xff0c;有上千年之久的功效。因为当你们打坐进入永恒时&#xff0c;时间停止了。这不只是两个半小时&#xff0c;而是百千万亿年…

为什么3d重制变换模型会变形?---模大狮模型网

3D建模和渲染过程中&#xff0c;设计师经常会遇到一个让人头疼的问题&#xff0c;那就是模型在进行重制变换后出现的意外变形。这种变形不仅影响了模型的外观和质量&#xff0c;也给设计工作带来了额外的麻烦。本文将深入探讨3D模型进行重制变换后出现变形的原因&#xff0c;帮…

回炉重造java----JVM

为什么要使用JVM ①一次编写&#xff0c;到处运行&#xff0c;jvm屏蔽字节码与底层的操作差异 ②自动内存管理&#xff0c;垃圾回收功能 ③数组下边越界检查 ④多态 JDK&#xff0c;JRE&#xff0c;JVM的关系 JVM组成部分 JVM的内存结构 《一》程序计数器(PC Register) 作用…