MVCC工作原理深入解析

news2024/11/17 1:27:31

一、事务概述

mysql事务是指一组命令操作,在执行过程中用来保证要么全部成功,要么全部失败。事务是由引擎层面来支持的,MyISM引擎不支持事务,InnoDB引擎支持事务。

事务具有ACID四大特性

原子性(Atomicity):指事务不可分割,要么全部成功,要么全部失败,不可能存在部分成功或部分失败的情况。如果执行某一条语句失败后,将会触发之前所有执行过的语句的回滚,因此靠的是undo log进行回滚。
一致性(Consistency):在事务执行前后,数据的完整性没有遭到破坏。一致性是mysql追求的最终目标,需要数据库层面与应用层面同时来维护。需要先满足原子性、隔离性与持久性,同时也需要应用层面做保障,即在应用层面对数据进行检验。
隔离性(Isolation):事务之前是隔离的,并发执行的事务之间不存在互相影响,mysql通过锁以及MVCC来保证隔离性。
持久性(Durability):事务一旦提交,那么对数据的操作就是永久性的,即使接下来数据库宕机也不会有影响。mysql是通过redo log来实现宕机恢复的,而binlog主要是用来误删恢复与主从复制的。

二、事务并发问题

数据库有读—读操作、读—写操作、写—写操作三种并发场景。三种场景下读写操作和写写操作可能会产生并发安全问题。

读—读操作:多线程同时进行读操作,这种场景下不会产生并发安全问题,不需并发控制。

读—写操作:两个线程在同一时刻分别进行读写操作,这种情况下可能会对数据库产生隔离性问题和出现脏读、幻读、不可重复读的问题。

写—写操作:两个线程同一时刻进行写操作,这种情况下可能会存在数据丢失的问题。

1、脏读

读取到别的事务未提交的数据被称为脏读。

A事务读取到了B事务未提交的数据,若B事务发生错误进行回滚操作,那么事务A读取到的数据就是脏数据。

这种情况常发生在转账场景下,如老板发错工资的情况

时间顺序转账事务查询事务
1开始事务
2        开始事务
3转账3.9万,未提交
4收入3.9万(涨工资了)
5提交事务
6发现转错了,进行事务回滚
7重新转账3.6万
8提交事务

分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读

2、幻读(Phantom Reads)

某个事务前后多次读取到的数据总量不一致被称为幻读。

事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,称为幻读

时间顺序

事务A

事务B

1开始事务
2第一次查询,数据总量100条
3开始事务
4新增100条数据
5提交事务
6第二次查询 数据总量为200条
7按照正确逻辑,事务A前后两次读取到的数据总量应该一致

3、不可重复读

某个事务范围内前后多次相同查询条件读取到的数据内容不一致被称为不可重复读。

事务A前后两次相同条件的查询时间跨度比较大,在其之间事务B对数据进行了修改操作,导致事务A第二次查询获取的数据与第一次不同(数据不重复了),称为不可重复读

时间顺序

事务A

事务B

1        开始事务
2第一次查询 小红语文成绩90
3开始事务
4更改小红语文成绩为95
5第二次查询,小红语文成绩95
6按照正确逻辑,事务A前后两次读取到的数据应该一致

三、事务隔离级别

事务隔离级别就是在不同程度上解决脏读、幻读、不可重复读的问题。数据库事务隔离级别有四种,由低到高分别为读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、串行化(Serializable)。

1、读未提交(Read Uncommitted)

读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。在这种隔离级别下所有事务都能读取其他事务未提交的数据。

读取其他事务未提交的数据会造成脏读。因此在此隔离级别下不能解决脏读、幻读和不可重复读。

读未提交可能会产生脏读的现象,那么怎么解决脏读呢?那就是使用读已提交。

2、读已提交(Read Committed)

在这种隔离级别下,所有事务只能读取其他事务已经提交的内容

能够彻底解决脏读的现象。但在这种隔离级别下,会出现一个事务的前后多次的查询中却返回了不同内容的数据的现象,也就是出现了不可重复读。

这是大多数数据库系统默认的隔离级别,例如Oracle和SQL Server,但mysql不是。

已提交可能会产生不可重复读的现象,我们可以使用可重复读。

3、可重复读(Repeatable Read)

在这种隔离级别下,所有事务前后多次的读取到的数据内容是不变的

也就是某个事务在执行的过程中,不允许其他事务进行update操作,但允许其他事务进行add操作,造成某个事务前后多次读取到的数据总量不一致的现象,从而产生幻读。

这才是mysql的默认事务隔离级别

可重复读在特殊情况下依然会产生幻读,此时我们可以使用串行化来解决。

4、串行化(Serializable)

在这种隔离级别下,所有的事务顺序执行,所以他们之间不存在冲突,从而能有效地解决脏读、不可重复读和幻读的现象。

但是安全和效率不能兼得,串行化会大大降低数据库的性能,一般不使用这种级别。

通过表格来表示四种不同隔离级别能够处理的问题

隔离级别脏读不可重复读幻读加锁读
读未提交
读已提交
可重复读否(InnoDB除外)
串行化

以上所说的隔离级别及当前级别存在的问题只是一种规范,不同的数据库厂商可以有不同的实现。

例如在mysql的可重复读的级别上,使用临键锁的方式就已经解决了幻读的问题。

四、MVCC工作原理

mysql为了实现以上隔离级别,提出了LBCC(Lock-Based Concurrent Control,基于锁的并发控制)与MVCC(Multi-Version Concurrent Control,基于多版本的并发控制)。

在LBCC中,读写冲突,会使用诸如记录锁、间隙锁与临键锁等锁来实现数据的并发安全,因此读写性能不高。

MVCC是为了解决事务中读-写操作并发安全问题的无锁并发控制技术,是通过数据库记录的隐式字段undo log日志和ReadView来实现的。

1、MVCC解决的问题

1. 并发读—写操作:可以解决读写并发阻塞问题,使读操作不阻塞写操作,写操作不阻塞读操作。

2. 解决脏读、不可重复读、幻读等事务隔离性问题

3. MVCC采用乐观锁方式实现,降低了死锁的概率

2、实现原理

MVCC是通过数据库的undo log版本链和ReadView方式来实现的无锁并发控制的。在InnoDB引擎下读已提交和可重复读隔离级别都是基于MVCC进行并发事务控制。

以下面4个事务为例进行解析MVCC实现原理和执行过程。

事务A、B、C对数据进行修改操作,事务D进行两次查询操作,第一次是在事务B修改后未commit前查询,第二次是在事务C修改后未commit前查询。

2.1 undo_log版本链

undo log

undo log主要用于事务回滚时恢复原来的数据。mysql在执行sql语句时,会将一条逻辑相反的日志保存到undo log中。因此,undo log中记录的也是逻辑日志,主要记录insert、update、delete相关操作的数据。

当sql语句为insert时,会在undo log中记录本次插入的主键id。等事务回滚时,delete此id即可。

当sql语句为update时,会在undo log中记录修改前的数据。等事务回滚时,再执行一次update,得到原来的数据。

当sql语句为delete时,会在undo log中记录删除前的数据。等事务回滚时,insert原来的数据即可。

数据库事务四大特性中的原子性,即事务具有不可分割性,要么全部成功,要么全部失败,其底层就靠undo log实现。在某一步执行失败时,会对之前事务的语句进行回滚。

行隐藏列

在数据库中的每一行上,除了存放真实的数据以外,还存在着3个隐藏列——row_id、trx_id与roll_pointer。

row_id(主键id)

若当前表有整数类型的主键,则row_id就是主键的值。若没有整数类型的主键,则mysql会按照字段顺序选择一个非空的整数类型的唯一索引作为row_id。如果mysql没有找到,则会自动生成一个自动增长的整数作为row_id。

trx_id(事务编号)

当一个事务开始执前,mysql会为这个事务分配一个全局自增的事务id。之后该事务对当前行进行的增、删、改操作时,都会将自己的事务id记录到trx_id中。

roll_pointer(回滚指针)

事务对当前行进行修改时,会将旧数据写入进undo log中,再将新数据写入当前行,且当前行的roll_pointer指向刚生成的undo log,因此可以通过roll_pointer找到该行的前一个版本。

undo log版本链

当一直有事务对该条数据进行修改时,就会一直生成undo log日志,最终会将这些undo log日志形成undo log版本链。

上面的例子事务A、B、C修改id=1088的数据行生成的undo log版本链如下

每一个事务对该行修改时,都会生成一个undo log,用于保存之前的版本,之后再将新版本的roll_pointer指向刚才生成的undo log。因此roll_pointer可以将这些不同版本的undo log串联起来,形成undo log版本链。

2.2 ReadView

ReadView是“快照读”SQL执行时MVCC提取数据的依据。首先需要理解下快照读和当前读。

快照读:就是最普通的Select查询的SQL语句,不包括select... for update 和select... lock in share mode。

当前读:读取的数据库记录,都是当前最新的版本,会对当前读取的数据进行加锁,防止其他事务修改数据。是悲观锁的一种操作。如下操作都是当前读:

  • select lock in share mode (共享锁)
  • select for update (排他锁)
  • update (排他锁)
  • insert (排他锁)
  • delete (排他锁)
  • 串行化事务隔离级别

在mysql中只有在快照读的情况下才会使用MVCC,尤其是在InnoDB引擎RR-可重复读隔离级别下会区分对待,快照读使用的是MVCC进行数据读取,当前读使用的是next-key lock锁(行锁+间隙锁)来实现数据读取。快照读和当前读在RR隔离级别下 对数据提取方式是完全不同的

在事务每一次执行快照读或事务初次执行快照读时,会生成一致性视图ReadView。

ReadView的作用是判断undo log版本链中哪些数据对当前事务可见。

ReadView包含几个重要的参数

m_ids:当前活跃的事务编号集合

min_trx_id:最小活跃事务编号(最小未提交事务编号)

max_trx_id:预分配事务编号,当前最大事务编号+1

create_trx_id:ReadView创建者的事务编号

ReadView生成

在RC-读已提交和RR-可重复读隔离级别下生成ReadView方式是不同的。

RC-读已提交

在RC-读已提交隔离级别下,每一次执行快照读时都会生成一个新的ReadView。

在RC隔离级别下,根据上面例子的事务执行为例,生成的ReadView过程

RR-可重复读

在RR-可重复读隔离级别下,仅在第一次执行快照读时生成ReadView,后续快照读复用第一次生成的ReadView。

在RR隔离级别下,根据上面例子的事务执行为例,生成的ReadView过程

ReadView数据提取过程

事务在执行快照读时,会从undo log历史版本链中从最新版本开始往前比对,通过一系列的规则,根据快照版本中的trx_id字段和ReadView来确定该版本对于当前事务是否可见。具体规则如下:

a. 若trx_id < min_trx_id,说明该版本对于当前事务(ReadView)来说,是已提交事务生成的,那么对于当前事务是可见的。

b. 若trx_id >= max_trx_id,说明该版本对于当前事务(ReadView)来说,是“将来”的事务生成的,那么对于当前事务是不可见的。

c. 若min_trx_id<= trx_id < max_trx_id,需进一步判断trx_id是否在m_ids集合列表中

        i. 若 trx_id在m_ids中,说明该版本是由还未提交的事务生成的,对于当前事务不可见。

        ii. 若trx_id不在m_ids中,说明该版本是已提交的事务生成的,对于当前事务可见。

d. 若trx_id=create_trx_id,即当前对比版本是由当前事务生成的,那么此版本对于当前事务当然可见。

e. 如果当前对比版本不可见,则通过roll_pointer找到上一个版本进行对比,直到找到可见版本或找不到任何一个可见版本。

RC-读已提交

根据上面执行的例子,RC隔离级别下生成了两个不同的ReadView。在比对数据时,两个ReadView中的m_ids不同,最终就会出现两个结果。

ReadView1与undo log比对结果

ReadView2与undo log比对结果

通过上面两个比对结果可知,两次快照读发生了不可重复读的问题,故在RC隔离级别下是无法解决不可重复读的问题。

RR-可重复读

RR-可重复读隔离级别下只生成一次ReadView,后续快照读都是复用之前的ReadView,所以通过ReadView复用的方式就可以解决不可重复读的情况。

RR-可重复读隔离级别下正常情况下通过MVCC版本控制的方式可以解决幻读的问题。因为作为ReadView在没有发生变化的前提下,mysql在中间执行过程中,无论是产生了新增、修改还是删除的话对于事务4都是不可见的。因此在连续多次快照读情况下ReadView会产生复用,没有幻读问题。

当两次快照读之间存在当前读,ReadView就会重新生成,导致产生幻读。举例如下

事务B先进行查询,获取ReadView1 显示1088-张三。

事务A 插入数据张三,同名不同人,此时事务1对应的undo_log为两条。

接着事务B执行update操作,当前读后再进行快照读是生成了ReadView2,但ReadView1和ReadView2的提取数据不同了。这种情况下在同一个事务B中前后两次相同的select查询结果不同的,产生了幻读。

产生幻读的问题就是在同一事务中 前后两次快照读中间加入了当前读的操作导致出现了幻读。

五、总结

从以上的分析中我们可以看出来,MVCC指的就是在使用RC-读已提交、RR-可重复读这两种隔离级别的事务在执行普通的SEELCT操作时访问记录的版本链的过程,这样子可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。 

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

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

相关文章

XSS靶场(1-11关)

目录 简述xss xss第1关 xss第2关 ​编辑 xss第3关 xss第4关 xss第5关过滤了 on script xss第6关 xss第7关 xss第8关 xss第9关 xss第10关 xss11关 我把源代码靶场放到了最顶端 简述xss XSS攻击通常指的是通过利用网页开发时留下的漏洞&#xff0c;通过巧妙的方法…

【数据结构与算法 | 图篇】最小生成树之Prim算法

1. 前言 普里姆算法&#xff08;Prims Algorithm&#xff09;是一种用于寻找加权无向图中的最小生成树&#xff08;Minimum Spanning Tree, MST&#xff09;的贪心算法。 最小生成树是指对于一个给定的无向图&#xff0c;连接所有顶点且边的总权重最小的生成树。 2. 算法步骤 …

Spring Boot 核心配置

一、 Spring Boot配置文件分类 SpringBoot是基于约定的&#xff0c;所以很多配置都有默认值&#xff0c;但如果想使用自己的配置替换默认配置的话&#xff0c;就可以使用application.properties或者application.yml&#xff08;application.yaml&#xff09;进行配置 applicat…

外贸市场开发【越南】

​作为“新兴之秀” 越南的经济发展可谓是突飞猛进 并在2022年&#xff0c;成为全亚洲经济增长最快速经济体 经济的增长也让越南的地位变得不一样了 一起来看看外贸人聚焦的东南亚排名前五的越南 越南社会主义共和国&#xff0c;通称越南&#xff0c;是位于东南亚的中南半岛…

谷粒商城实战笔记-213-商城业务-认证服务-整合短信验证码服务

文章目录 一&#xff0c;开通阿里云云市场短信服务1&#xff0c;阿里云开通免费短信服务并调试2&#xff0c;整合短信服务2.1 下载HttpUtils代码2.2 开发调用短信服务的组件2.3 测试 HttpUtils代码 这一节主要内容是整合短信发送服务。 一&#xff0c;开通阿里云云市场短信服务…

多输入多输出 | Matlab实现CPO-BP冠豪猪优化算法优化BP神经网络多输入多输出预测

多输入多输出 | Matlab实现CPO-BP冠豪猪优化算法优化BP神经网络多输入多输出预测 目录 多输入多输出 | Matlab实现CPO-BP冠豪猪优化算法优化BP神经网络多输入多输出预测预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 多输入多输出 | Matlab实现CPO-BP冠豪猪优化…

CUDA-MODE课程笔记 第6课: 如何优化PyTorch中的优化器

我的课程笔记&#xff0c;欢迎关注&#xff1a;https://github.com/BBuf/how-to-optim-algorithm-in-cuda/tree/master/cuda-mode CUDA-MODE课程笔记 第6课: 如何优化PyTorch中的优化器 课程内容 上面三张Slides讲述了运行时间&#xff08;runtime&#xff09;和内存使用&…

ChatGPT 3.5/4.0 新手使用手册(详细版)

1. 什么是 ChatGPT&#xff1f; ChatGPT是由 OpenAI 开发的先进人工智能语言模型&#xff0c;能够理解并生成自然语言文本。它可以帮助你进行写作、回答问题、提供建议&#xff0c;甚至参与对话。ChatGPT 3.5 和 4.0 是两个不同版本&#xff0c;它们都拥有强大的语言处理能力&…

sublime text 4 安装(含激活码)安装破解汉化 Sublime Text 4 的操作指南

sublime text 4 安装&#xff08;含激活码&#xff09; 一、下载步骤 官网地址&#xff1a;Sublime Text - the sophisticated text editor for code, markup and prosehttps://link.zhihu.com/?targethttps://www.sublimetext.com/ windows下载链接&#xff1a;Thank You -…

【数据结构算法经典题目刨析(c语言)】使用数组实现循环队列(图文详解)

&#x1f493; 博客主页&#xff1a;C-SDN花园GGbond ⏩ 文章专栏&#xff1a;数据结构经典题目刨析(c语言) 目录 一.题目描述 二.解题思路 1.循环队列的结构定义 2.队列初始化 3.判空 4.判满 5.入队列 6.出队列 7.取队首元素 8.取队尾元素 三.完整代码实…

【Datawhale AI夏令营第四期】魔搭-AIGC方向 Task03笔记 原神风格Lora尝试 ComfyUI Lora微调 补充选学内容

【Datawhale AI夏令营第四期】魔搭-AIGC方向 Task03笔记 原神风格Lora尝试 ComfyUI Lora微调 首先我们继续推进网课进度。 https://space.bilibili.com/1069874770 传送门 WorldArt锦书产品介绍&#xff1a; 我属实是没想到这个产品居然还可以用作遗迹鉴定和名家笔记仿写这样…

2.2算法的时间复杂度与空间复杂度——经典OJ

本博客的OJ标题均已插入超链接&#xff0c;点击可直接跳转~ 一、消失的数字 1、题目描述 数组nums包含从0到n的所有整数&#xff0c;但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗&#xff1f; 2、题目分析 &#xff08;1&#xff09;numsS…

Java流程控制之循环结构(附案例说明)超详细

循环结构&#xff1a;根据循环条件&#xff0c;重复性执行某段代码 for循环 while 循环 do-while 循环 凡是循环 就有4个要素&#xff1a; 1、初始化要素2、循环条件&#xff08;一定是boolean类型的变量或表达式&#xff09; 3、循环体 4、迭代部分 for 循环格式 for(1;2;…

阿里声音项目Qwen2-Audio的部署安装,在服务器Ubuntu22.04系统——点动科技

阿里声音项目Qwen2-Audio的部署安装&#xff0c;在服务器Ubuntu22.04系统——点动科技 一、ubuntu22.04基本环境配置1.1 更换清华Ubuntu镜像源1.2 更新包列表&#xff1a;2. 安装英伟达显卡驱动2.1 使用wget在命令行下载驱动包2.2 更新软件列表和安装必要软件、依赖2.2 卸载原有…

JAVA学习之知识补充(下)

六&#xff1a;File类与IO流&#xff1a; 这里给出三种常见的初始化方法&#xff1a; 通过文件路径初始化: File file new File("C:/example/test.txt");这种方法用于创建一个文件对象&#xff0c;该文件对象表示指定路径的文件或目录。例如&#xff1a;File fil…

Zookeeper服务注册及心跳机制详解

ZooKeeper提供了一种类似于文件目录的结构来保存key值&#xff0c;其提供了四种key类型&#xff0c;分别是持久节点&#xff0c;临时节点&#xff0c;持久有序节点&#xff0c;临时有序节点。其中临时节点的特性是当创建此节点的会话断开时&#xff0c;节点也会被删除。这一特性…

xss.function靶场(easy)

文章目录 第一关Ma Spaghet!第二关Jefff第三关Ugandan Knuckles第四关Ricardo Milos第五关Ah Thats Hawt第六关Ligma第七关Mafia第八关Ok, Boomer 网址&#xff1a;https://xss.pwnfunction.com/ 第一关Ma Spaghet! 源码 <!-- Challenge --> <h2 id"spaghet&qu…

一文掌握 Web 测试:功能、界面、兼容与安全的综合测试指南!

随着Web技术的不断演进&#xff0c;测试除了对应用的功能性、界面美观性、跨平台兼容性的基本要求外、安全性和性能的要求也逐步增高。因此&#xff0c;全面、系统的测试思维和策略成为了保证Web应用高质量的关键因素。本篇文章将从功能测试、界面测试、兼容性测试和安全测试四…

AI歌手-五月天(声音转换)

重磅推荐专栏: 《大模型AIGC》 《课程大纲》 《知识星球》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经…

JavaScript - 数组对象中实用好玩的reduce方法

JavaScript中reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行)&#xff0c;将其结果汇总为单个返回值。 语法&#xff1a; arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue]) 参数配置&#xff1a; 参数名描述cal…