深入理解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.hqwc.cn/news/698373.html

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

相关文章

Netty-面试题(中)(五十)

关于零拷贝和堆外内存 Java在将数据发送出去的时候&#xff0c;会先将数据从堆内存拷贝到堆外内存&#xff0c;然后才会将堆外内存再拷贝到内核态&#xff0c;进行消息的收发&#xff0c;代码如下: 所以&#xff0c;我们发现&#xff0c;假如我们在收发报文的时候使用直接内存&…

地产人的福音!VR全景分屏对比,让装修施工一目了然

现如今&#xff0c;VR全景已成为地产行业不可或缺的应用工具&#xff0c;从地产直播到楼市VR地图&#xff0c;从效果图到水电家装施工记录&#xff0c;可以说整个地产行业的上下游生态中都可以看到720云VR全景的身影。 以720云VR全景作为直播载体 VR全景高清矩阵地图等多种内容…

使用python开发的闭运算调试器

使用python开发的开运算调试器 简介效果代码 简介 用来调试闭运算效果的小工具&#xff0c;滑动条可以控制滤波核的大小&#xff0c;用来查看不同滤波核下的闭运算效果。 效果 代码 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayou…

为什么要用机架式液冷负载

机架式液冷负载是一种先进的散热技术&#xff0c;它通过将冷却液直接引入服务器的热源&#xff0c;实现对服务器内部组件的高效散热。与传统的空气冷却方式相比&#xff0c;机架式液冷负载具有更高的散热效率、更低的能耗和更长的设备寿命等优点。以下是使用机架式液冷负载的几…

Saas详解

1. 什么是Saas SaaS&#xff08;Software-as-a-Service&#xff09;&#xff0c;简单点理解就是软件即服务&#xff0c;即通过网络提供软件服务。 在SaaS模型中&#xff0c;用户不需要在本地安装软件&#xff0c;而是通过网络&#xff08;通常是浏览器&#xff09;访问应用程…

ValueError: source code string cannot contain null bytes

导入pandas报如下错误&#xff1a; 解决&#xff1a; pandas好像只支持到3.8&#xff0c;我的python是3.10&#xff0c;改成3.7就能正常使用了

Spire.PDF for .NET【文档操作】演示:将多个 PDF 文件中的选定页面合并为一个

使用 Spire.PDF&#xff0c;您不仅可以将多个 PDF 文件合并为一个文件&#xff0c;还可以从源文件中选择特定页面并将它们合并为一个 PDF 文档。以下代码片段演示了相同的内容。 Spire.PDF for .NET 是一款独立 PDF 控件&#xff0c;用于 .NET 程序中创建、编辑和操作 PDF 文档…

高血压患者可以吃什么?不可以吃什么?

点击文末领取揿针的视频教程跟直播讲解 随着生活水平的提高&#xff0c;越来越多的人得上了“新四高”——高血压、高血脂、高血糖、高尿酸。 一旦惹上这些慢性病&#xff0c;那就得「管住嘴」了~但究竟什么能吃、什么不能吃&#xff0c;还是有很多人不清楚 高血压患者应该遵…

实在智能AI+RPA:引领数字化转型的超自动化智能体

引言 在数字化时代&#xff0c;企业面临着前所未有的挑战和机遇。数字化转型不仅是企业生存的需要&#xff0c;更是实现持续增长和创新的关键。AIRPA作为数字化转型的重要驱动力&#xff0c;正帮助企业实现业务流程的自动化和智能化&#xff0c;从而提升效率、降低成本、增强竞…

树莓派指令raspi-gpio控制gpio引脚输出输入

raspi-gpio help 帮助 raspi-gpio get 获取引脚状态 level是电平&#xff0c; func是功能 raspi-gpio set 设置gpio引脚 raspi-gpio set 20 pu op dl 20是GPIO20&#xff0c; pu是上拉&#xff0c; op是output输出&#xff0c; dl是drive low低电平

React 第三十章 React 和 Vue 描述页面的区别

面试题&#xff1a;React 和 Vue 是如何描述 UI 界面的&#xff1f;有一些什么样的区别&#xff1f; 标准且浅显的回答&#xff1a; React 中使用的是 JSX&#xff0c;Vue 中使用的是模板来描述界面 前端领域经过长期的发展&#xff0c;目前有两种主流的描述 UI 的方案&#xf…

Q1季度电饭煲家电行业线上市场(京东天猫淘宝)销售数据排行榜

鲸参谋监测的2024年Q1季度线上电商平台&#xff08;天猫淘宝京东&#xff09;电饭煲家电销售数据已出炉&#xff01; 今年Q1季度&#xff0c;电饭煲销售成绩不如预期。根据鲸参谋数据显示&#xff0c;今年Q1季度在线上电商平台&#xff08;淘宝天猫京东&#xff09;电饭煲销量…