InnoDB存储引擎对MVCC的实现

MVCC

MVCC的目的

在搞清楚MVCC之前,我们要搞懂一个问题,MVCC到底解决的是什么问题?

我用一句话概括,那就是为了解决读-写可以一起的问题!

在我们的印象里,InnoDB可以读读并发,不能读写并发,或者写写并发

这是很正常的想法,因为如果读写并发的化,会有并发问题

而对于写写并发来说,我们还是得加锁,这是没办法改的

但是对于读-写并发来说,我们就可以用MVCC来解决

所以,综上所述,MVCC就是为了解决读-写并发,提高性能.

更准确的说,它是针对读操作的!和写操作关系不大

所以在MVCC的加持上,如果要解决并发问题?
读 + MVCC
写 + 锁

解决哪些并发问题?

先说结论:
对于读已提交

MVCC 解决了脏读

对于可重复读
MVCC 解决了脏读 + 不可重复读 + 幻读


然后其次,我们要搞清楚,有四种隔离级别,我相信大家都知道

读未提交 什么都没解决
读已提交 解决了脏读
可重复读 解决了脏读 + 不可重复读
串行 解决了脏读 +不 可重复读 + 幻读

这我相信你也是滚瓜烂熟

那么对于MVCC来说,它实际解决了哪些问题呢? 这个我们就得分情况了,因为MVCC也是贴合四种隔离级别的!我先说这个就是为了有一个大致的方向

需要注意的是,对于MVCC来说,读未提交 和 串行是束手无策的

你想一想就知道,读未提交连脏读都可以容忍,它还要你MVCC来干嘛,直接读就可以了
对于串行来说,它的读 + 写都是串行化的,不管你读还是写,都要排队,根本没有读 - 写并发的情况

所以,我们需要明确一点,MVCC是针对读已提交 + 可重复读

想清楚这一点,我们再来分别看,MVCC解决的问题,你可能会问,既然你都是贴合四种隔离级别的,那不就是对应隔离级别解决的问题嘛,不是的,这里就是有一个特殊,所以我才会拿来说

对于读已提交

MVCC 解决了脏读

对于可重复读
MVCC 解决了脏读 + 不可重复读 + 幻读

那么比较特殊的一点就是,对于可重复读来说,多解决了一个幻读的问题

综上所述,我们的结论就是,MVCC是针对读已提交 + 可重复读,并且在可重复读的情况下,还多解决了一个幻读问题

如何实现的?

读到这里,你就很好奇了,它是如何实现的呢?

其实很简单,就是一句话,MVCC读的是副本,不是当前的当前正在并发的数据

我们来反过来想,为什么会出现脏读 + 不可重复读,就是因为事务在并发的时候,有可能会修改你正在读的数据,这就导致,读第二次的时候会出现不一致

那我们该如何解决呢?也很简单,就是在事务一开始的时候,保存一份当前数据的副本,这样就算其他事务修改了,它修改的是数据库的数据,而不是我们的副本,这样不就避免了这两个问题吗?

顺着这个思路想,如果每次事务一开始的时候,我们都要取保存一份当前数据的副本,那如果有上亿的读的访问的话,那不是内存炸了?

所以,我们得用MySQL现成的功能来实现这个副本的功能!

所以,自然而然你会想到,Undo log,只有它存着历史的数据,那就是刚刚好的

至此,我们大致就可以引出了三个依赖
第一,隐藏字段
第二,Undo log
第三,Read View

Undo log就不用说了,就是为了生成副本的
Read View 也好说,不过是副本的官方的名字
对于隐藏字段来说,我们就得说一说了

对于聚簇索引来说,他有必要的隐藏列,
trx_id: 事务id
roll_pointer: 指针

事务id,就不用多说了,你需要一个标识来标识事务
对于roll_pointer来说,它和Undo log就会有联系,就是一个指针嘛,我们直接看图就能懂了,为什么需要roll_pointer,还有Undo log实际上长什么样子
在这里插入图片描述
一看就懂,不就是链表嘛,第一个就是最新的记录,后边的都是历史记录

此时,你就大概能明白,为什么需要隐藏字段了把,其实也不是很重要,一笔带过就行

我们特别要注意的是,Read View 副本的结构,这才是我们需要研究的东西
因为我们不熟啊

Read View结构

对于Read View我们先看图,它的大致结构如下

在这里插入图片描述
实际,它记录的不是实际的数据,而是一群活跃的事务id

什么叫活跃的事务id呢?就是那些在并发的时候,在那捣乱的事务,为啥要记录呢?很好理解嘛,在Undo log中,只要有这些捣蛋鬼做的记录,我们就不看,读那些已经提交的记录

结构

creator_trx_id: 创建Readview的事务id
trx_ids: 活跃的事务id列表
up_limit_id: 最小的事务ID
low_limit_id: 下一个最大的事务ID

这里需要特别注意,up_limit_id是最小的
low_limit_id是最大的

还有一点是,这里的最小最大的选取标准是不一样的
这里举个例子,比较容易懂

比如说,事务id列表中,有三个事务id, 1,3,5

我们要假设MySQL只有这三个事务id,其他没有,暗含的意思是,这里的id = 5,是当前生成的最大的事务id

那么up_limit_id 就是1
low_limit_id 是当前生成过的最大的事务id + 1 也就是 5 + 1 = 6

假设事务5提交了,此时的事务列表是1,3
low_limit_id 还是6,因为它类似于Auto_increment 自增id的意思,它会记录当前生成过的最大的事务ID

ReadView的规则

我们研究这个规则,实际上就是研究MVCC的核心规则了

我们得对应着Undo log的版本链来看,版本链就是历史记录

如下
在这里插入图片描述
然后就着
ReadView的结构来看

在这里插入图片描述

事务开始,生成一个ReadView,
然后当前事务,要读取的时候,此时就会去查看两个东西 ReadView + Undo log

查看Undo log就是顺着链表一个一个往下看,找到符合条件的就返回

符合的条件就是规则

第一,如果此时查看的记录trx_id = creator_trx_id,符合条件,读此记录
意思就是,当前记录就是我们自己事务干的事,ReadView是我们当前事务自己生成的,自己读自己的,当然没问题

第二,如果不是自己的,就检查,ReadView的trx_ids,也就是事务id列表
如果发现,当前访问的记录,就是这些捣蛋鬼其中一个干的,那就是不符合条件,如果发现不在这群事务id里边,说明,当前读的是,已经提交了的记录,符合条件

第三,需要注意的是,对于第二,来说,当前的记录的trx_id是介于
up_limit_id < 当前 事务id < low_limit_id,

up_limit_id,和low_limit_id,是一个范围,还有一个功能就是快速的判断

如果,最新的记录是小于up_limit_id的,那么直接就是返回当前记录

为什么呢?因为事务id是单调递增的,和主键id一样,如果我们最新的记录都提交了,那更早的记录是不是都提交了,返回返回没问题!

如果,最新的记录是大于等于low_limit_id的,那么不符合条件,往下找
为什么呢? 因为,如果此时最新的记录是大于low_limit_id的,意思就是这个记录它是在我们事务生成ReadView之后生成的,也就是

我们生成ReadView
然后紧接着,有一个事务进来修改了

那更不可能读这条记录了,比迟到的人还迟到.

综上所述,我们总结一下
trx_id = creator_trx_id,可以读
当trx_id < up_limit_id,可以读
当trx_id >= low_limit_id,不可以读
当up_limit_id <= trx_id < low_limit_id的时候,查看当前记录的id是不是在trx_ids里边,如果在的话,说明这是捣蛋鬼干的,不可以读,如果不存在,可以读

再总结一下,我们只要记住什么时候不可以读

当trx_id >= low_limit_id 的时候,不可以读
当trx_id在范围中间,且当前的记录id,在trx_ids里边,不可以读
其他情况都可以读

读已提交 和 可重复读的不同流程

因为,MVCC要贴合这两个隔离级别解决的问题,所以,他们的流程会有点不一样

对于读已提交来说,事务开始,每一次select都会去获取ReadView
对于可重复读来说,事务开始,到结束,只会获取一次ReadView

这个想想就能明白,读已提交,没有解决不可重复读问题,那么每一次读ReadView的时候,有可能有一些事务就提交了,会读到多的数据

可重复读的时候,由于至始至终,只看一个ReadView,那就是事务开始的时候的ReadView,那可想而知,不会有可重复读的问题,还有幻读的问题了,因为,只看一个ReadView,就算多一万遍,读到的记录还是不会多不会少,所以这也是为什么对于可重复读来说,它可以解决幻读问题了.

总结

所以我们最后来总结一下

MVCC的目的是什么?

为了解决读-写并发,但是只针对读已提交 + 可重复读

MVCC是怎么实现的?

第一,隐藏字段-> 事务id + 指针
第二,Undo log
第三,ReadView

如何实现这个问题,就转换成ReadView如何做校验?

对于读已提交,每次select的时候,获取一个ReadView
对于可重复读,事务开始的时候,获取一个ReadView

做实际的校验

到最后,做校验的问题,就转换成校验的规则是什么?

如果,trx_id >= low_limit_id,不符合条件
如果,up_limit_id <= trx_id < low_limit_id,并且trx_id在trx_ids中,不符合条件
其他情况都符合

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

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

相关文章

构建安全的REST API:OAuth2和JWT实践

引言 大家好&#xff0c;我是小黑&#xff0c;小黑在这里跟咱们聊聊&#xff0c;为什么REST API这么重要&#xff0c;同时&#xff0c;为何OAuth2和JWT在构建安全的REST API中扮演着不可或缺的角色。 想象一下&#xff0c;咱们每天都在使用的社交媒体、在线购物、银行服务等等…

Sqli-labs靶场第16关详解[Sqli-labs-less-16]自动化注入-SQLmap工具注入

Sqli-labs-Less-16 #自动化注入-SQLmap工具注入 SQLmap用户手册&#xff1a;文档介绍 - sqlmap 用户手册 以非交互式模式运行 --batch 当你需要以批处理模式运行 sqlmap&#xff0c;避免任何用户干预 sqlmap 的运行&#xff0c;可以强制使用 --batch 这个开关。这样&#xff0…

GC机制以及Golang的GC机制详解

要了解Golang的GC机制,就需要了解什么事GC,以及GC有哪几种实现方式 一.什么是GC 当一个电脑上的动态内存不再需要时&#xff0c;就应该予以释放&#xff0c;以让出内存&#xff0c;这种内存资源管理&#xff0c;称为垃圾回收&#xff08;Garbage Collection&#xff09;&#x…

最长上升子序列(LIS)简介及其例题分析

一.最长上升子序列&#xff08;LIS&#xff09;的相关知识 1.最长上升子序列&#xff08;Longest Increasing Subsequence&#xff09;&#xff0c;简称LIS&#xff0c;也有些情况求的是最长非降序子序列&#xff0c;二者区别就是序列中是否可以有相等的数。假设我们有一个序…

【论文笔记】Initializing Models with Larger Ones

Abstract 介绍权重选择&#xff0c;一种通过从预训练模型的较大模型中选择权重子集来初始化较小模型的方法。这使得知识从预训练的权重转移到更小的模型。 它还可以与知识蒸馏一起使用。 权重选择提供了一种在资源受限的环境中利用预训练模型力量的新方法&#xff0c;希望能够…

【Linux】软件管理yum | 编辑器vim | vim插件安装

目录 1. Linux软件管理yum 1.1 什么是软件包 1.2 查看软件包 1.3 如何安装软件 1.4 如何卸载软件 2. Linux编辑器vim 2.1 vim的基本概念 2.2 vim的基本操作 2.3 vim正常模式命令集 2.4 vim末行模式命令集 2.5 简单vim配置 2.6 插件安装 1. Vim-Plug 3. coc.nvim …

如何自己系统的学python

学习Python是一项很好的投资&#xff0c;因为它是一种既强大又易于学习的编程语言&#xff0c;适用于多种应用&#xff0c;如数据分析、人工智能、网站开发等。下面是一个系统学习Python的步骤建议&#xff1a; 基础准备 安装Python&#xff1a; 访问Python官网下载最新版本的…

微信小程序-生命周期

页面生命周期 onLoad: 页面加载时触发的方法&#xff0c;在这个方法中可以进行页面初始化的操作&#xff0c;如获取数据、设置页面状态等。 onShow: 页面显示时触发的方法&#xff0c;在用户进入页面或从其他页面返回该页面时会调用此方法。可以在此方法中进行页面数据刷新、动…

Onenote软件新建笔记本时报错:无法在以下位置新建笔记本

报错现象&#xff1a; 当在OneNote软件上&#xff0c;新建笔记本时&#xff1a; 然后&#xff0c;尝试重新登录微软账户&#xff0c;也不行&#xff0c;提示报错&#xff1a; 解决办法&#xff1a; 打开一个新的记事本&#xff0c;复制粘贴以下内容&#xff1a; C:\Users\Adm…

Mysql中的事务

什么是事务&#xff1a; 多条sql语句&#xff0c;要么全部成功&#xff0c;要么全部失败。 事务的特性&#xff1a; 1&#xff1a;原子性(Atomic)&#xff1a; 组成一个事务的多个数据库操作是一个不可分割的原子单元&#xff0c;只有所有操作都成功&#xff0c;整个事务才会…

C++调用lua函数

C 调用Lua全局变量(普通) lua_getglobal(lua, "width");int width lua_tointeger(lua,-1);lua_pop(lua,1);std::cout << width << std::endl;lua_close(lua); 这几行代码要放到lua_pcall(lua, 0,0,0);之后才可以. C给lua传递变量 lua_pushstring(lua, …

Python 操作 Excel,如何又快又好?

➤数据处理是 Python 的一大应用场景&#xff0c;而 Excel 则是最流行的数据处理软件。因此用 Python 进行数据相关的工作时&#xff0c;难免要和 Excel 打交道。Python处理Excel 常用的系列库有&#xff1a;xlrd、xlwt、xlutils、openpyxl ◈xlrd &#xff0d; 用于读取 Exce…

Rocky 9 安装 R-CytoTRACE

官网给出的详细指南&#xff0c;只是可能大家打不开或者懒得去看E文。 第一步&#xff0c;下载CytoTRACE安装包。 wget https://cytotrace.stanford.edu/CytoTRACE_0.3.3.tar.gz 第二步&#xff0c;打开R或者Rstudio-server # 安装依赖包 if (!requireNamespace("Bioc…

视频云平台——搭建SRS5平台支持GB28181视频流的推送

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;新的征程&#xff0c;我们面对的不仅…

谨用ArrayList中的subList方法

谨用ArrayList中的subList方法 规范一&#xff1a; ArrayList 的 subList 结果不可强转成 ArrayList&#xff0c;否则会抛出 ClassCastException 异常&#xff1a; public static void test7() {List<Integer> list new ArrayList<>();list.add(1);list.add(2);…

JavaWeb—— SpringBootWeb综合案例(登录功能、登录校验、异常处理)

案例-登录认证 目录 案例-登录认证1. 登录功能1.1 需求1.2 接口文档1.3 思路分析1.4 功能开发1.5 测试 2. 登录校验2.1 问题分析2.2 会话技术2.2.1 会话技术介绍2.2.2 会话跟踪方案2.2.2.1 方案一 - Cookie2.2.2.2 方案二 - Session2.2.2.3 方案三 - 令牌技术 2.3 JWT令牌2.3.1…

程序员眼中的“祖传代码”

引言 在IT界&#xff0c;特别是在Java项目中&#xff0c;“祖传代码”通常指的是那些经过长时间积累、由多位开发者共同维护、且蕴含深厚技术沉淀的代码片段或模块。这些代码可能存在于项目的核心模块&#xff0c;也可能是一些辅助性的工具类。它们承载着项目的历史&#xff0…

Matlab 多项式插值(曲线拟合)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 由于对曲线拟合有些兴趣,这里就找了一些资料从最基本的方法来看一下曲线拟合的效果: 二、实现代码 % **********

python科学计算库之Numpy库的使用的简单习题

Numpy库 Numpy&#xff08;Numerical Python的缩写&#xff09;是一个开源的Python库&#xff0c;用于进行科学计算。它提供了一个高性能的多维数组对象&#xff08;ndarray&#xff09;及用于处理这些数组的各种工具和函数。由于其高效和灵活的数据结构以及丰富的功能&#x…

Google 地图 API 教程--干货(1/2)

Google Maps API 教程 在本教程中我们将学习如何使用谷歌地图API V3创建交互式地图。 什么是 API? API = 应用程序编程接口(Application programming interface)。 API(Application Programming Interface,应用编程接口)其实就是操作系统留给应用程序的一个调用接口,…