【MySQL】MVCC(多版本并发控制)详解

MVCC

MVCC概述

MVCC,全称 Multi-Version Concurrency Control ,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。

MVCC就是在ReadCommitted(读已提交)、RepeatableRead(可重复读) 隔离级别,不加锁的情况下,解决并发事务的脏读、幻读和不可重复读问题。

MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。

什么是当前读、快照读?

当前读: 读到的数据都是最新的数据,像排他锁(又称写锁updateinsertdeleteselect for update共享锁(又称读锁select lock in share mode,这些操作都是当前读。

快照读: 可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。像不加锁的select操作就是快照读,如果隔离级别是串行化,快照读会退化成当前读。

数据库并发场景

  • 读-读: 不存在任何问题。
  • 读-写: 有线程安全问题,可能出现脏读、幻读、不可重复读。
  • 写-写: 有线程安全问题,可能存在更新丢失等。

MVCC(多版本并发控制)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。

MVCC实现原理

MVCC 的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3个隐式字段,undo日志版本链 , Read View 来实现的。

三个隐式字段

数据库中的每行记录都有DB_TRX_ID(事务id)DB_ROLL_PTR(回滚指针)DB_ROW_ID(隐藏主键)这三个字段。

DB_TRX_ID(事务id)DB_ROLL_PTR(回滚指针)DB_ROW_ID(隐藏主键)
记录创建这条记录,最后一次修改该记录事务的ID。每处理一个事务,其值自动 +1回滚指针,指向这条记录的上一个版本(存储于 rollback segment 里)隐含的自增 ID(隐藏主键),如果数据表没有主键,InnoDB 会自动以DB_ROW_ID产生一个聚簇索引

undo日志版本链

undo日志版本链是指一行数据被多个事务依次修改过后,在每个事务修改完后,Mysql会保留修改前的数据undo回滚日志,并且用两个隐藏字段DB_TRX_ID(事务id)DB_ROLL_PTR(回滚指针)把这些undo日志串联起来形成一个历史记录版本链。

undo日志分类:

undo log 主要分为两种:

  • insert undo log 代表事务在 insert 新记录时产生的 undo log,只在事务回滚时需要,并且在事务提交后可以被立即丢弃
  • update undo log 事务在进行 updatedelete时产生的 undo log ;不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被 purge 线程统一清除。
undo日志版本链流程图

1)事务A,在employee表插入了一条数据,name为zhangsan、age为22、主键id为1、事务id为100、回滚指针和隐式主键为null。这条日志会在insert undo log中。

在这里插入图片描述
2)事务B对employee表主键id为1的记录做了修改,修改了name字段为lisi。(在事务B之前都没有事务修改过这条数据)

  • 拷贝改行数据到undo log中,作为旧纪录,既在 undo log 中有当前行的拷贝副本。
  • 修改name字段为lisi,假设事务id自增到101,回滚指针指向拷贝的副本。

在这里插入图片描述

3)事务C对employee表主键id为1的记录做了修改,修改了age字段为18。

  • 在拷贝数据到undo log中,发现这行数据已经有undo日志版本链了。修改后的数据,假设事务id为102。回滚指针就会指向undo日志版本链最后修改的那一条记录。

在这里插入图片描述

Read View 读视图

在可重复读隔离级别,当事务开启,执行任何查询sql时会生成当前事务的一致性视图read-view该视图在事务结束之前永远都不会变化(如果是读已提交隔离级别在每次执行查询sql时都会重新生成read-view)这个视图由执行查询时所有未提交事务id数组(数组里最小的id为min_id)和已创建的最大事务id+1(max_id)组成,事务里的任何sql查询结果需要从对应版本链里的最新数据开始逐条跟read-view做比对从而得到最终的快照结果。

Read View 主要是用来做可见性判断的,可见性算法如下 (trx_id事务id)

绿色部分( trx_id<min_id ):已提交的事务
红色部分( trx_id>max_id ):未开始的事务
黄色部分(min_id <=trx_id<= max_id):活跃的事务。

  1. 如果 row trx_id 落在绿色部分( trx_id<min_id ),表示这个版本是已提交的事务生成的,这个数据是可见的;
  2. 如果 rowtrx_id等于当前事务的trx_id,这个数据是可见的;
  3. 如果 rowtrx_id 落在红色部分( trx_id>=max_id ),表示这个版本是由将来启动的事务生成的,是不可见的;
  4. 如果 rowtrx_id 落在黄色部分(min_id <=trx_id<max_id),那就包括两种情况
    a. 若 rowtrx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见
    b. 若 rowtrx_id 不在视图数组中,表示这个版本是已经提交了的事务生成的,可见

对于删除的情况可以认为是update的特殊情况,会将版本链上最新的数据复制一份,然后将trx_id修改成删除操作的trx_id,同时在该条记录的头信息(record header)里的(deleted_flag)标记位写上true,来表示当前记录已经被删除,在查询时按照上面的规则查到对应的记录如果delete_flag标记位为true,意味着记录已被删除,则不返回数据。

关于readview和可见性算法的原理解释

readview和可见性算法其实就是记录了sql查询那个时刻数据库里提交和未提交所有事务的状态。

要实现RR隔离级别,事务里每次执行查询操作readview都是使用第一次查询时生成的readview,也就是都是以第一次查询时当时数据库里所有事务提交状态来比对数据是否可见,当然可以实现每次查询的可重复读的效果了。

要实现RC隔离级别,事务里每次执行查询操作readview都会按照数据库当前状态重新生成readview,也就是每次查询都是跟数据库里当前所有事务提交状态来比对数据是否可见,当然实现的就是每次都能查到已提交的最新数据效果了。

RepeatableRead(可重复读)MVCC可见性算法示例

假设有一张account表,account表有主键idnamebalance三个字段。最开始这张表里面的数据是:
在这里插入图片描述

1)事务trx_id = 100,修改了balance字段为200,此时事务100还未提交
2)事务trx_id = 200,修改了balance字段为300,此时事务200还未提交
3)事务trx_id = 300,修改了balance字段为500,此时事务300已经提交

此时的undo log日志版本链如下图:

在这里插入图片描述
4)事务trx_id = 400,执行select语句查询id=1,balance字段的数据,数据库为该行数据生成一个Read View读视图,假设数据看没有其它事务了,此时的readview数组:[100,200],min_id:100,max_id:400+1 = 401

此时的row最新的trx_id为300,因为事务400未作修改操作。从undo log日志版本链最后修改的,一直往上查询。根据上面的可见性算法分析:

  1. 先判断trx_id<min_id(300<100)不满足条件
  2. 再判断trx_id=create_trx_id(当前事务id),(300=400)不满足条件
  3. 再判断trx_id>=max_id(300>=401)不满足条件
  4. 再判断min_id <=trx_id<max_id(100<=300<401)满足条件
  5. trx_id=300,很显然不在readview数组[100,200]中,可见

得出结论:事务400查询的数据balance字段为500

5)事务trx_id = 200,又修改了balance字段为800,此时事务200已提交

此时的undo log日志版本链如下图:

在这里插入图片描述

6)事务trx_id = 400,又执行select语句查询id=1,balance字段的数据。在可重复读隔离级别下,不会重新生成Read View读视图,还是用的之前的Read View读视图。此时的readview数组:[100,200],min_id:100,max_id:400+1 = 401

此时的row最新的trx_id为200,同步骤4分析:

  1. 先判断trx_id<min_id(200<100)不满足条件
  2. 再判断trx_id=create_trx_id(当前事务id)(200=400)不满足条件
  3. 再判断trx_id>=max_id(200>=401)不满足条件
  4. 再判断min_id <=trx_id<max_id(100<=200<401)满足条件
  5. trx_id=200,很显然在readview数组[100,200]中,不可见

上面判断出最新修改的undo log日志不满足,继续往上查询,同理可以推断出,事务400查询的数据balance字段为500

ReadCommitted(读已提交)MVCC可见性算法示例

读已提交和可重复读是类似的,读已提交就是每次执行select,就会重新生成Read View读视图。

上面的我就不复制了,唯一不同的就是步骤6,

6)事务trx_id = 400,又执行select语句查询id=1,balance字段的数据。当前的活跃事务就只剩下事务100了,此时的readview数组:[100],min_id:100,max_id:400+1 = 401

此时的row最新的trx_id为200:

  1. 先判断trx_id<min_id(200<100)不满足条件
  2. 再判断trx_id=create_trx_id(当前事务id),(200=400)不满足条件
  3. 再判断trx_id>=max_id(200>=401)不满足条件
  4. 再判断min_id <=trx_id<max_id(100<=200<401)满足条件
  5. trx_id=200,很显然在readview数组[100,200]中,可见

得出结论:事务400查询的数据balance字段为800

参考文章:
【MySQL笔记】正确的理解MySQL的MVCC及实现原理

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

echarts实现不展示X轴Y轴轴线、刻度

今日工作中需要实现折线图的简图&#xff0c;就是只看个大概趋势不展示具体坐标&#xff0c;查阅了文档记录一下。 initCharts(_id, _name, yAxisData, _unit){if(this[_id]) this[_id].clear();this[_id] $echarts.init(document.getElementById(_id));const options {grid…

基于Springboot+Vue的社区医院管理系统

基于SpringbootVue的社区医院管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 医生列表 医生详情 登录界面 管理员界面 医生界面 摘要 社区医院管…

van-dialog弹窗异步关闭-校验表单

van-dialog弹窗异步关闭 有时候我们需要通过弹窗去处理表单数据&#xff0c;在原生微信小程序配合vant组件中有多种方式实现&#xff0c;其中UI美观度最高的就是通过van-dialog嵌套表单实现。 通常表单涉及到是否必填&#xff0c;在van-dialog的确认事件中直接return是无法阻止…

POE也收费了

一直通过POE在用chatgpt&#xff0c;今天下午发现要收费了…

二百零三、Flume——Flume实时采集数据频率为1s的高频率Kafka数据直接写入ODS层表的HDFS文件路径下

一、目的 在离线数仓中&#xff0c;需要用Flume去采集Kafka中的数据&#xff0c;然后写入HDFS中。 由于每种数据类型的频率、数据大小、数据规模不同&#xff0c;因此每种数据的采集需要不同的Flume配置文件。玩了几天Flume&#xff0c;感觉Flume的使用难点就是配置文件 二、…

【MATLAB源码-第78期】基于matlab的可见光通信不同调制方式(OOK,PPM,DPPM,DHPIM)误码率,信道容量分析。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 可见光通信&#xff08;VLC&#xff0c;Visible Light Communication&#xff09;是一种利用可见光作为信号载体的通信技术。在VLC中&#xff0c;常用的调制方式包括OOK&#xff08;On-Off Keying&#xff09;、PPM&#xff…

【C++初阶】三、类和对象(面向过程、class类、类的访问限定符和封装、类的实例化、类对象模型、this指针)

相关代码gitee自取&#xff1a; C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 【C初阶】二、入门知识讲解 &#xff08;引用、内联函数、auto关键字、基于范围的for循环、指针空值nullptr&#xff09;-CSDN博客 一 . 面向过程和面向对象初步认识 C语言 -- 面向…

【力扣题:循环队列】

文章目录 一.题目描述二. 思路解析三. 代码实现 一.题目描述 设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好…

Oracle-动态sql学习笔记,由易至难讲解七个例子

本文章的内容来源于对oracle课堂上讲的内容做出的笔记 静态sql和动态sql 静态sql&#xff1a; 静态 SQL 是在编译时写死的 SQL 语句&#xff0c;即在程序编写阶段&#xff0c;SQL 语句已经被固定下来。 特点&#xff1a; 1.预编译&#xff1a; SQL 语句在程序编译时就会被…

C#中.NET 7.0 Windows窗体应用通过EF访问新建数据库

目录 一、 操作步骤 二、编写EF模型和数据库上下文 三、移植&#xff08;Migrations&#xff09;数据库 四、编写应用程序 五、生成效果 前文已经说过.NET Framework4.8 控制台应用通过EF访问已经建立的和新建的数据库。 前文已经说过.NET 6.0 控制台应用通过EF访问…

ajax异步传值以及后端接收参数的几种方式

异步传值 第一种呢&#xff0c;也是最简单的一种&#xff0c;通过get提交方式&#xff0c;将参数在链接中以问号的形式进行传递 // 前台传值方法 // 触发该方法调用ajaxfunction testAjax(yourData) {$.ajax({type: "get", // 以get方式发起请求url: "/yo…

海康设备、LiveNVR等通过GB35114国密协议对接到LiveGBS GB28181/GB35114平台的详细操作说明

一、LiveNVR通过GB35114接入LiveGBS 1.1 开启LiveGBS 35114功能 信令服务livecms.ini配置文件中[sip]增加一行gm1 启动LiveCMS 1.2 生成设备端证书 我们用LiveNVR做为设备端向LiveGBS注册&#xff0c;这里先生成LiveNVR的设备证书&#xff0c;并将LiveNVR的设备证书给LiveGB…

绩效考核管理项目|记录1

项目用C#winformSQL Server写的&#xff0c;现在记录一下学习到的新东西。 winform工具 splitContainer&#xff1a;分割出两个容器&#xff0c;能添加面板之类的工具 treeview&#xff1a;展示标签页的分层集合&#xff08;用户管理、基数管理......&#xff09;&#xff0…

GZ038 物联网应用开发赛题第6套

2023年全国职业院校技能大赛 高职组 物联网应用开发 任 务 书 &#xff08;第6套卷&#xff09; 工位号&#xff1a;______________ 第一部分 竞赛须知 一、竞赛要求 1、正确使用工具&#xff0c;操作安全规范&#xff1b; 2、竞赛过程中如有异议&#xff0c;可向现场考评…

蓝桥杯 大小写转换

islower/isupper函数 islower和issupper是C标准库中的字符分类函数&#xff0c;用于检查一个字符是否为小写字母或大写字母 需要头文件< cctype>,也可用万能头包含 函数的返回值为bool类型 char ch1A; char ch2b; //使用islower函数判断字符是否为小写字母 if(islower(…

短路语法 [SUCTF 2019]EasySQL1

打开题目 输入字符的时候啥也不回显。只有输入数字的时候页面有回显 但是当我们输入union&#xff0c;from&#xff0c;sleep&#xff0c;where&#xff0c;order等&#xff0c;页面回显nonono&#xff0c;很明显过滤了这些关键词 最开始我的思路是打算尝试双写绕过 1;ununion…

【SpringBoot】序列化和反序列化介绍

一、认识序列化和反序列化 Serialization&#xff08;序列化&#xff09;是一种将对象以一连串的字节描述的过程&#xff1b;deserialization&#xff08;反序列化&#xff09;是一种将这些字节重建成一个对象的过程。将程序中的对象&#xff0c;放入文件中保存就是序列化&…

220V交流转直流的简易电源设计

220V交流转直流的简易电源设计 设计简介设计原理电路图变压器电路交流转直流电路3.3V电源接口电路 PCB3D图 实践检验 设计简介 通过模拟电路的相关知识&#xff0c;尝试将220V的交流电转化为我们指定电压的直流电。 设计原理 将220V交流电转化为直流电的方法常用的有通过变压器…

京东数据挖掘(京东数据采集):2023年Q3电脑行业数据分析报告

近年来&#xff0c;在远程办公、远程教育等需求的刺激下&#xff0c;电脑的销售增长较为显著。不过&#xff0c;随着市场的成熟乃至饱和&#xff0c;电脑销售市场也逐渐出现增长困难、需求疲软等问题。 2023年第三季度&#xff0c;电脑市场的出货量同比下滑。根据鲸参谋电商数据…

lc121. 买卖股票的最佳时机

一次遍历&#xff0c;一边遍历一边修改买入的价格&#xff0c;一边比较取得最大利润 public class BuyAndSellStocks {public static void main(String[] args) {int[] arr {7,1,5,3,6,4};int[] arr1 {7,6,4,3,1};System.out.println(buyAndSellStocks(arr));System.out.pri…