一文精通MVCC机制

news2024/11/16 9:35:02

MVCC(Multi-Version Concurrency Control)多版本并发控制机制

使用串行化隔离级别时,mysql会将所有的操作加锁互斥,来保证并发安全。这种方式必然降低并发性能。mysql在读已提交可重复读隔离级别下,对一行数据的读和写两个操作默认是不会通过加锁互斥来保证隔离性,避免了频繁加锁互斥。那么具体是如何实现的呢?首先要了解两个概念。

准备

建表语句

CREATE TABLE `product` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `price` int DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;

undo日志版本链

  1. 我们向product表插入一条数据

INSERT INTO mysql_demo.product (id, name, price) VALUES (1, 'apple', 10);

此时mysql会同时向undo日志里写入一条记录。 trx_id为插入操作的事务id。这里随便写了一个80,意思一下。 roll_pointer后面再说。

  1. 这时候又来了一个事务,对数据进行了修改。比如事务id 300,修改price为20。此时mysql同样会在undo日志里写入一条记录。并且roll_pointer会指向前一条记录

  1. 以此类推,后续又有新的事务来操作这条记录,就会形成一条版本链,这条链就是undo日志版本链

每条数据对应着有一个undo日志版本链。

对于insert和update操作,mysql会向undo日志里添加一条记录。select操作不会产生记录。

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

在来看下什么是read view。

一致性试图read view机制

read view的生成

  • 可重复读隔离级别:事务开启后,首次执行任何select时会生成当前事务的read-view,在事务结束前不会变化。

  • 读已提交隔离级别:事务开启后,每次执行select时都会重新生成read-view。

read view的组成

这个视图由执行查询时所有未提交事务id数组(数组里最小的id为min_id)已创建的最大事务id(max_id)组成。

我们来举个例子。

  1. Transaction 80: 开启事务,插入一条记录。并且commit;

  1. Transaction 100:开启事务,执行update。生成事务id 100。这里需要注意begin和select不会生成事务id,所以加了一条无关的update,生成事务id。update内容可以忽略。

begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个修改操作InnoDB表的语句,事务才真正启动,才会向mysql申请事务id

mysql内部是严格按照事务的启动顺序来分配事务id的

  1. Transaction 200:同上

  1. Transaction 300:把价格修改成20了。并且commit了。

  1. select 1: select 不生成事务id。 事务开启后,首次执行任何select时会生成当前事务的read-view。

  1. Transaction 400:把价格修改成18了。

read view的组成 = 未提交事务id数组(数组里最小的id为min_id) + 已创建的最大事务id(max_id)组成

此时未提交事务id有100,200(80 已经提交了)。最小的id为100。 已创建的最大事务id为300。(注意read view 是在第5步生成的,此时还没有Transaction 400)

因此 read view为[100,200],300 min_id为100 ,max_id为300。 [100,200] 为视图数组。

此时对应的undo日志版本链如下

那么read view 的作用是什么呢?

read view的作用

根据上面的结果,我们可以将事务进行分类。因为事务的id是有序递增的。所以我们可以得出以下结论

  • 因为未提交事务的最小id(min_id)为100,所以小于100的事务都是已提交的。( Transaction 80)

  • 因为已创建的最大事务id(max_id)为300,所以大于300的区域都是未开启事务。 (Transaction 400) 未开启理解为在执行select的时候没有开启。

  • 介于min_id和max_id之间的事务,包含了未提交和已提交的事务。 (Transaction 100,200,300)

那么mysql是如何通过read view和undo日志版本链实现并发事务之间的隔离的呢?那就需要看下版本链比对规则了。

版本链比对规则

事务里的每一条select都需要从对应版本链里的最新数据开始逐条跟read-view做比对,按照比对规则得到最终的快照结果。下面我们来看下版本链比对规则。

  1. 如果 row 的 trx_id 落在绿色部分( trx_id

  1. 如果 row 的 trx_id 落在灰色部分( trx_id>max_id ),表示这个版本是由将来启动的事务生成的

  1. row 的 trx_id 就是当前自己的事务是可见的;

  1. 否则不可见;

  1. 如果 row 的 trx_id 落在黄色部分(min_id <=trx_id<= max_id),那就包括两种情况

  1. 若 row 的 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,

  1. 若 row 的 trx_id 就是当前自己的事务是可见的

  1. 否则不可见;

  1. 若 row 的 trx_id 不在视图数组中,表示这个版本是已经提交了的事务生成的,可见。

知道了版本链的比对规则,下面我们通过实例来看下,mysql的MVCC机制是如何工作的。

实战演练

可重复读Repeatable-Read(RR)

我们先以可重复读Repeatable-Read(RR)为例

可重复读隔离级别:事务开启后,首次执行任何select时会生成当前事务的read-view,在事务结束前不会变化。

案例一

我们先以上面的情况为例来进行分析。此时的情况如下:

  • read view为 [100,200],300

  • undo日志版本链如下

  • 套用版本链比对规则

  1. 首先在版本链中找到最新数据。

  1. Transaction 300,trx_id = max_id。此时继续比对, trx_id 不在视图数组中,可见

  1. 返回Transaction 300记录的数据信息。price = 20;

案例二

Transaction 400,在第10行执行了一次update。

Transaction 100,在第11,12行执行了两次update。然后select 1 13行执行了一次select。 我们来分析下这个select。

  • 因为RR隔离级别首次执行任何select时会生成当前事务的read-view,在事务结束前不会变化。所以read view为 [100,200],300。没有变化。

  • undo日志版本链如下

  • 套用版本链比对规则

  1. 首先在版本链中找到最新数据。

  1. Transaction 100,trx_id = min_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. 第二行Transaction 100,分析同上

  1. 第三行Transaction 400, trx_id > max_id,不可见。

  1. Transaction 300,trx_id = max_id。此时继续比对,trx_id 不在视图数组中,可见

  1. 返回Transaction 300记录的数据信息。price = 20;

案例三

继续向下Transaction 100,在第15行commit。Transaction 200,在第15,16行执行了两次update。然后select1 17行执行了一次select。 我们来分析下这个select。

  • 因为RR隔离级别首次执行任何select时会生成当前事务的read-view,在事务结束前不会变化。所以read view为 [100,200],300。没有变化。

  • undo日志版本链如下

  • 套用版本链比对规则

  1. 首先在版本链中找到最新数据。

  1. Transaction 200,min_id < trx_id < max_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. 下一行Transaction 200,分析同上.

  1. Transaction 100,trx_id = min_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. 下一行Transaction 100,分析同上。

  1. 下一行Transaction 400, trx_id > max_id,不可见。

  1. Transaction 300,trx_id = max_id。此时继续比对,trx_id 不在视图数组中,可见

  1. 返回Transaction 300记录的数据信息。price = 20;

案例四

继续select2 17行执行了一次select。 我们来分析下这个select。

  • RR隔离级别首次执行任何select时会生成当前事务的read-view。read view为 [200,400],400。

  • undo日志版本链如下

  • 套用版本链比对规则

  1. 首先在版本链中找到最新数据。

  1. Transaction 200,trx_id = min_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. 下一行Transaction 200,分析同上.

  1. Transaction 100,trx_id < min_id。表示这个版本是已提交的事务生成的,这个数据是可见的;

  1. 返回 price = 16。

案例五

我们再来看一下如果select1 如果有update操作(update操作会创建事务id,我们假设是 500)。Transaction 500 此时是如何读取到更新后的数据的。

来分析下15行。

  • RR隔离级别首次执行任何select时会生成当前事务的read-view,在事务结束前不会变化。read view为 [100,200],300。

  • undo日志版本链如下

  • 套用版本链比对规则

  1. 首先在版本链中找到最新数据。

  1. Transaction 400,trx_id > max_id(read view是第一次select时生成的,此时max_id仍然是 300)。表示这个版本是由将来启动的事务生成的,是不可见的

  1. Transaction 500,trx_id > max_id。表示这个版本是由将来启动的事务生成的,但row 的 trx_id 就是当前自己的事务是可见的;所以可见

  1. 返回 price = 8。

结论:通过以上案例,我们可以知道。 MVCC机制在RR中首次查询时会固定read view。后续和其他事务隔离开了,其他事务对数据的操作不会影响到当前事务。

读已提交Read-Committed(RC)

我们再以读已提交Read-Committed(RC)为例

读已提交隔离级别:事务开启后,每次执行select时都会重新生成read-view。

案例一

第9行没有变化,我们来看第13行。

read view的组成 = 未提交事务id数组(数组里最小的id为min_id) + 已创建的最大事务id(max_id)组成

未提交事务id数组 100,200,400 ; min_id 100 ; max_id 400

  • read view为 [100,200,400],400。

  • undo日志版本链如下

  • 套用版本链比对规则

  1. 首先在版本链中找到最新数据。

  1. Transaction 100,trx_id = min_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. 下一行Transaction 100,分析同上.

  1. Transaction 400, trx_id = max_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. Transaction 300,min_id < trx_id< max_id。不在视图数组中,表示这个版本是已经提交了的事务生成的,可见。

  1. 返回 price = 20。

案例二

来看第17行。

read view的组成 = 未提交事务id数组(数组里最小的id为min_id) + 已创建的最大事务id(max_id)组成

未提交事务id数组 200,400 ; min_id 200 ; max_id 400

  • read view为 [200,400],400。

  • undo日志版本链如下

  • 套用版本链比对规则

  1. 首先在版本链中找到最新数据。

  1. Transaction 200, trx_id = min_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. 同上

  1. Transaction 100, trx_id

  1. 返回 price = 16。

OK,就分析到这里吧。希望对你有所帮助!

读已提交Read-Committed(RC)

我们再以读已提交Read-Committed(RC)为例

读已提交隔离级别:事务开启后,每次执行select时都会重新生成read-view。

案例一

第9行没有变化,我们来看第13行。

read view的组成 = 未提交事务id数组(数组里最小的id为min_id) + 已创建的最大事务id(max_id)组成

未提交事务id数组 100,200,400 ; min_id 100 ; max_id 400

  • read view为 [100,200,400],400。

  • undo日志版本链如下

  • 套用版本链比对规则

  1. 首先在版本链中找到最新数据。

  1. Transaction 100,trx_id = min_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. 下一行Transaction 100,分析同上.

  1. Transaction 400, trx_id = max_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. Transaction 300,min_id < trx_id< max_id。不在视图数组中,表示这个版本是已经提交了的事务生成的,可见。

  1. 返回 price = 20。

案例二

来看第17行。

read view的组成 = 未提交事务id数组(数组里最小的id为min_id) + 已创建的最大事务id(max_id)组成

未提交事务id数组 200,400 ; min_id 200 ; max_id 400

  • read view为 [200,400],400。

  • undo日志版本链如下

  • 套用版本链比对规则

  1. 首先在版本链中找到最新数据。

  1. Transaction 200, trx_id = min_id。继续分析 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见。

  1. 同上

  1. Transaction 100, trx_id

  1. 返回 price = 16。

OK,就分析到这里吧。希望对你有所帮助!

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

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

相关文章

【Unity3d】Unity与iOS之间通信

在unity开发或者sdk开发经常遇到unity与移动端原生层之间进行通信&#xff0c;这里把它们之间通信做一个整理。 关于Unity与Android之间通信&#xff0c;参考【Unity3d】Unity与Android之间通信 Unity调用Objective-C 主要分三个步骤&#xff1a; (一)、在xcode中定义要被u…

php学习笔记

之前看过php的基础教学视频&#xff0c;了解了一下&#xff0c;自己没有上手实践&#xff0c;现在为了项目需要&#xff0c;需要扎实学习一下&#xff0c;所以做一下笔记吧。 php学习笔记1.基础2.动态网站的开发学习2.1会员管理系统1.基础 之前看过一个4小时的基础视频&#x…

Go 数组和切片反思

切片的底层数据结构是数组&#xff0c;所以&#xff0c;切片是基于数组的上层封装&#xff0c;使用数组的场景&#xff0c;也完全可以使用切片。 类型比较 我看到 go 1.17 有对切片和数组转换的优化&#xff0c;禁不住纳闷&#xff0c;有什么场景是必须数组来完成的呢&#x…

vue项目第七天

项目中模块操做业务使用ajax&#xff08;需要使用接口认证&#xff09;修改封装的findData发送ajax请求管理员列表内部搜索业务复用之前的findData 方法即可实现整个查询业务。实现退出业务在下拉菜单上添加事件以及属性。用户退出登录&#xff0c;二次登录系统菜单可能不存在的…

linux环境搭建私有gitlab仓库

搭建之前&#xff0c;需要安装相应的依赖包&#xff0c;并且要启动sshd服务(1).安装policycoreutils-python openssh-server openssh-clients [rootVM-0-2-centos ~]# sudo yum install -y curl policycoreutils-python openssh-server openssh-clients [rootVM-0-2-centos ~]…

(API)接口测试的关键技术

接口测试也就是API测试&#xff0c;从名字上可以知道是面向接口的测试活动。所以在讲API测试之前&#xff0c;我们应该说清楚接口是什么&#xff0c;那么接口就是有特定输入和特定输出的一套逻辑处理单元&#xff0c;而对于接口调用方来说&#xff0c;不用知道自身的内部实现逻…

Spring 中经典的 9 种设计模式

1.简单工厂(非23种设计模式中的一种) 2.工厂方法 3.单例模式 4.适配器模式 5.装饰器模式 6.代理模式 7.观察者模式 8.策略模式 9.模版方法模式 Spring中涉及的设计模式总结 1.简单工厂(非23种设计模式中的一种) 实现方式&#xff1a; BeanFactory。Spring中的BeanFa…

Android 初代 K-V 存储框架 SharedPreferences,旧时代的余晖?

本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 提问。 前言 大家好&#xff0c;我是小彭。 SharedPreferences 是 Android 平台上轻量级的 K-V 存储框架&#xff0c;亦是初代 K-V 存储框架&#xff0c;至今被很多应用沿用。 有的…

【C语言】大小端字节序问题

一、大小端字节序问题 大小端是由CPU决定的&#xff0c;大小端可以理解为字节顺序&#xff0c;所以大小端全称叫大端字节序、小端字节序。其实大端、小端这两个词是从《格列佛游记》里出来的。《格列佛游记》有一段讲的是吃鸡蛋是从大的那头敲开还是小的那头敲开的问题&#xf…

拯救了大批爬虫程序员,因为一个简单的神器

相信大家应该都写过爬虫&#xff0c;简单的爬虫只需要使用 requests 即可。遇到复杂的爬虫&#xff0c;就需要在程序里面加上请求头和参数信息。类似这种&#xff1a;我们一般的步骤是&#xff0c;先到浏览器的网络请求中找到我们需要的请求&#xff0c;然后将请求头和参数信息…

CI/CD --- 什么才是真正的自动化平台

近2年在软件开发中比较火的两个术语&#xff0c;一个是敏捷开发&#xff0c;另外一个就是CI/CD了&#xff1b;敏捷开发顾名思义就是“以用户的需求进化为核心&#xff0c;采用迭代、循序渐进的方法进行软件开发”。那CI/CD&#xff08;Continuous Integration、 Continuous Del…

自抗扰控制ADRC之微分器TD

目录 前言 1 全程快速微分器 1.1仿真分析 1.2仿真模型 1.3仿真结果 1.4结论 2 Levant微分器 2.1仿真分析 2.2仿真模型 2.3仿真结果 3.总结 前言 工程上信号的微分是难以得到的&#xff0c;所以本文采用微分器实现带有噪声的信号及其微分信号提取&#xff0c;从而实现…

0216-0218复习:继承

目录 继承 一、基本介绍 二、示意图 三、基本语法 四、入门案例 父类 子类1 子类2 main方法 五、继承细节 第一条 第二条 第三条 第四条 ​编辑 第五条 第六条 第七条 第八条 第九条 第十条 六、继承本质 七、练习题 第三题 继承 一、基本介绍 继承可以…

RAY - 小记

文章目录关于 RAYRAY 结构关于 RAY Ray is a unified framework for scaling AI and Python applications. Ray consists of a core distributed runtime and a toolkit of libraries (Ray AIR) for accelerating ML workloads. RAY 是一个简单、通用的分布式计算框架。 RAY 解…

TikTok话题量超30亿,这款承载美好记忆的剪贴簿引发讨论

回忆风剪贴簿在TikTok引起关注小超在浏览超店有数后台时发现&#xff0c;有一款平平无奇的剪贴簿的种草视频爆火&#xff0c;在24h内收获了9.9K点赞&#xff0c;播放量更是突破了100W&#xff0c;直接冲到了【种草视频飙升榜】第六名的位置&#xff0c;并且这个数字目前仍在继续…

利用5G工业网关实现工业数字化的工业互联网解决方案

5G工业网关是一种用于将工业生产环境中的数据连接到工业互联网的解决方案。它可以利用高带宽、高速率、低时延的5G网络连接工业现场的PLC、传感器、工业设备和云端数据中心&#xff0c;从而实现工业数字化。 物通博联工业互联网解决方案 物通博联5G工业网关的使用步骤&#x…

XXL-JOB分布式任务调度框架(二)-策略详解

文章目录1.引言2.任务详解2.1.执行器2.2.基础配置3.路由策略(第一个)-案例4.路由策略(最后一个)-案例5.轮询策略-案例6.随机选取7.轮询选取8.一致性hash9.最不经常使用 (LFU)10.最近最久未使用&#xff08;LRU&#xff09;11.故障转移12.忙碌转移7.分片广播任务1.引言 本篇文章…

中外互免签证协定一览表(普通护照与公务普通护照)

普通护照&#xff1a;由公安部出入境管理机构或者公安部委托的县级以上地方人民政府公安机关出入境管理机构以及中华人民共和国驻外使馆、领馆和外交部委托的其他驻外机构签发&#xff0c;主要颁发给出国定居、探亲、访友、继承财产、留学、就业、旅游等因私事出国的中国公民。…

[REDIS]redis的一些配置文件

修改配置文件 vim /etc/redis/redis.conf目录 protected-mode tcp-backlog timeout tcp-keepalive daemonize pidfile loglevel databases 设置密码 maxclients maxmemory maxmemory-policy maxmemory-samples 默认情况下 bind127.0.0.1 只能接受本机的访问请求。在不写的情况…

算法导论【字符串匹配】—朴素算法、Rabin-Karp、有限自动机、KMP

算法导论【字符串匹配】—朴素算法、Rabin Karp、有限自动机、KMP朴素字符串匹配算法Rabin-Karp算法有限自动机KMP算法朴素字符串匹配算法 预处理时间&#xff1a;0匹配时间&#xff1a;O((n-m1)m) Rabin-Karp算法 预处理时间&#xff1a;Θ(m)&#xff0c;需要预先算出匹…