mysql的MVCC多版本并发控制机制(mysql并发情况下怎么解决)
itomcoil 2025-07-24 18:43 5 浏览
认识MVCC
MVCC 是英文Multi-Version Concurrency Control 多版本并发控制的首字母简拼。在上文MYSQL事务隔离级别中,我们已经知道,在可重复读的级别下,不管其他事务怎么修改一条数据,一个事务内查询同一条语句,不管查询几遍查到的数据都是一样的。他就是依靠mvcc机制来实现这样的效果。但是在可串行化级别下,mysql就是通过加锁互斥来保证隔离性,频繁地加锁互斥所以可串行化级别效率也是最低的。所以一般不用这个级别。所以,mvcc简单点理解就是不使用加锁,而是使用多版本控制来达到事务之间的隔离性,这样显然效率会比较高。
在mysql中除了可重复读用到mvcc机制之外,还有读已提交级别也用到mvcc机制。下面的介绍中默认是基于可重复读级别来介绍,读已提交级别也是大同小异在下文会分析他们的不同之处。
认识undo日志版本链
MVCC是多版本并发控制,那多版本是怎么做到的呢?多版本就是通过undo日志版本链来实现的。具体的说就是一行数据被多个事务依次修改过后,在每个事务修改完后,Mysql会保留修改前的数据undo回滚日志,并且会有两个隐藏字段一个是记录每个事物的事务ID(trx_id)一个是回滚指针(roll_pointer)指向上一个事务。undo日志版本链是对所有事务可见的,一行数据不管被哪几个事务修改,修改几次都会维护到undo日志中,并在undo日志中维护一个链条,目的是用于事务回滚(当前事务失败回滚到指针指向的上一个事务)或事务隔离(每个事务读到的版本有所不同)。
如上图,假设有一张账号表account,id=1的记录name初始值是lilei。
1. 被事务id为300的事务,修改成name=lilei300,指针指向事务80的记录
2. 又被事务为100的事务,修改成name=lilei1,指针指向事务300的记录
3. 事务为100的事务又进行修改成name=lilei2,指针指向事务100的记录
4. 再被事务为200的事务修改成name=lilei3,指针指向事务为100的记录
5. 最后还是在事务200中修改成name=lilei4,指针又指向上一条记录。
综上,每次对id=1的修改,记录下来并用指针指向上一次记录,形成了一个undo日志版本链。
认识read-view
read-view是一致性视图,如果说undo日志版本链是mvcc中的多版本体现,那read-view就是并发控制中必不可少的一环,因为每一个事务都会有他自己的readview。在可重复读隔离级别下,当事务开启,执行任何查询select语句的时候会生成当前事务的一致性视图read-view,该视图在事务结束之前都不会变化。这个read-view由执行查询时所有未提交事务id数组(数组里最小的id为min_id)和已提交的最大事务id(max_id)组成,事务里的任何sql查询结果需要从对应版本链里的最新数据开始逐条跟read-view做比对从而得到最终的结果。read-view是每个事务自己独有的而且是一成不变的,而undo日志版本链是所有事务共有一份的。他的结构类似这样
read-view[未提交事务id1,未提交事务id2....未提交事务idN],已提交最大事务idM
他的结构有两部分组成数组中是所有未提交的事务集合,数组外面和数组并列的是已提交的最大事务ID。
但是呢,在读已提交的级别下,read-view在事务中并不是一成不变的,而是每次查询都会生成一个新的read-view,这也是可重复读和读已提交的区别。
MVCC工作原理
上面分别介绍了undo日志版本链和read-view,他们之间的交互和关系就是MVCC的工作原理。
一个事务的查询,是从undo日志版本中最新的版本开始比对的,select语句产生后,readview就固定下来了。此时readview是已知的,undo版本日志链也是已知的,具体如下:
1. 如果当前比对的undo日志版本 trx_id小于未提交的事务ID也就是小于readivew数组中 最小的事务ID,表示这个版本是已提交的事务生成的,这个数据是可见的;就可以返回给客户端。
2.如果当前比对的undo日志版本版本trx_id刚好等于最大已提交的事务,说明是已提交的可直接返回给客户端。
3. 如果当前比对undo日志版本 trx_id大于最大提交的事务ID也就是readview中最大事务ID,表示这个版本是由未开始的事务生成的,是不可见的,那就要比对undo日志版本中的上一个版本。
4.如果当前比对的undo日志版本trx_id 是未提交的事务ID也就是在readview的数组中那说明这个版本是由还没提交的事务生成的,是不可见的,继续去比较上一个undo日志版本记录。
MVCC机制的实现就是通过read-view机制与undo版本链比对机制,使得不同的事务会根据数据版本链对比规则读取同一条数据在版本链上的不同版本数据。
案例分析
以上是通过理论层面认识mvcc机制,现在通过一个具体的案例加深对mvcc的理解。
假设account表有一条id=1,name=lilei的初始记录。
这里补充一点:begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个修改操作InnoDB表的语句,事务才真正启动,才会向mysql申请事务id,mysql内部是严格按照事务的启动顺序来分配事务id的。执行到第一条select语句的时候才会生成read-view。
案例1
假设当前数据库中有这样4个事务,事务100更新test表未提交,事务200更新test表未提交,事务300更新account表已提交,第一条select语句在事务300提交之后查询account表ID=1的记录。此时select 1中的第一条查询语句会产生readview,根据readview的生成规则,是由所有未提交的事务和已提交的最大事务组成的,所以他是这样的:
readview[100,200],300
100,200是当前未提交的事务,300是已提交的最大事务
这里面隐藏了两个信息:
1.事务id小于100的说明都是提交的事务
2.事务id大于300的都是未开始的事务
此时account=1的undo版本日志链是这样的:
先比较undo版本日志链中第1条记录,事务id=300,不在readview[100,200]数组中,在比对readview中最大事务ID(300)刚好相等,就返回这条版本记录name=lilei300,所以,selec 1中读的name=lilei300。
案例2
案例2在案例1的基础上进行,事务100,对account表进行两次更新但是未提交,select1,在事务100更新之后,又做了一次一模一样的查询。此时select语句的readview是多少呢?因为这是可重复读级别所以此时readview还是readview[100,200],300。而此时undo日志版本链是怎样的呢?如下图:
因为事务100对account表id=1记录做了两次修改,所有版本链多了两条记录,并且都指向上一个记录。还是老样子,从第1条记录开始比对,事务id=100,100跟readview[100,200],300相比是在数组中,是未提交的,在比对上一条记录,事务id还是等100跟readview[100,200],300相比还是在数组中,是未提交的在比对上一条记录,事务id=300跟readview[100,200],300不在数组中,在跟300对刚好相等,所以返回的还是lilei300,这就达到了可重复读的效果。
案例3
案例3还是基于前面两个案例的基础上,事务100此时对前面两个修改进行提交,事务200更新了两次,但是都没有提交。select 1中 又做了一次一模一样的查询,select2中另一个事务也做了一次一模一样的查询。那此时结果会是什么样子的呢?
先看此时的unodo日志版本链,因为undo日志版本链是对所有事务可见的:
先看select1中的第三条查询语句,跟之前一样,前面说过可重复读级别情况下readview是在第一条查询语句的时候就锁定了,所以readview还是readview[100,200],300。
此时先拿出undo日志版本链的第一条记录事务id=200,跟readview[100,200],300相比在数组中,是未提交的所以继续比对上一条记录,事务id还是等200,继续比对上一条事务id=100跟readview[100,200],300相比还是在数组中还是未提交的(尽管事务100此时提交了但是对select 1事务来讲事务100是未提交的,因为select 1 第一条查询语句开始的时候他是未提交的,select 1整个事务中readview是由第一次查询的时候决定了readview)所以继续比对上一条记录事务ID还是100,继续比对上一条事务ID=300,刚好是已提交的所以就返回,select1中的第三条语句读到的还是lilei300。即使事务100提交了,select 1中多次读到的数据都是lilei300有没有发现,这就是可重复读。
在看select2,select2中是第一次查询,此时锁定readview,readview的规则前面说过,是由当前所有未提交的事务和最大已提交事务组成,由于此时事务100已经提交了,所以,select2中的readview为:readview:[200], 300 。也是按undo日志版本链一次比对,先比对第一条,事务id=200和readview:[200], 300比较发现是在数组中是未提交的事务,继续比对上一条记录事务id还是200。继续比对上一条记录事务ID=100,诶,不在readview:[200], 300数组中,而且是小于200的说明是已提交的事务,此时就锁定这条记录,所以select2中查到的name=lilei2。
有没有发现,不管其他事务怎么改变值,select1中读到的都是lilei300,而且,select2中读到的是lilei2。这样就达到事务之间的隔离性和可重复读。
最后提一句,在读提交级别中,所有流程都是一样的,只是readview的生成时机有所区别,可重复读是在第一条查询语句开始的时候就锁定了readview,所以每一次读到的值都一样。而读提交级别的时候是每一次查询的时候都会重新生成readview,所以读提交每一次读到的都是最新值。
以上就是mysql中mvcc机制的大致原理,主要是利用undo日志版本链和readview两个机制保质事务的隔离性。
相关推荐
- Python高效数据处理——从基础方法到性能优化
-
数据处理是数据分析的核心环节,高效的数据处理方法能显著提升代码性能。本文将深入介绍Pandas中的各种数据处理技术,并分析它们的性能特点。使用apply方法应用自定义函数apply是Pandas中最灵...
- 正态分布-置信区间计算(正态90%置信区间)
-
统计学有两大主要分支,分别是描述性统计学和推断统计学。描述性统计学用于描述和概括数据的特征以及绘制各类统计图表。总体数据,往往因为数据量太大而难以被获取,所以就有了通过较小的样本数据推测总体特性的推断...
- 一篇文章搞定人工智能之深度学习创建训练数据集的方法
-
基础数据准备训练所需要的数据集合都存储在数据库中,还有部分文本文件首先对数据进行分类结构化存储[因为涉及到的是多分类问题]整理并存储原始数据集使用numpy将所有需要数据读取出来splitlines(...
- 向量搜索之 k-means 算法(annoy向量检索)
-
一直好奇向量数据库的索引是如何实现的,我们可以推断向量搜索的简单实现:把数据存入向量数据库时,会计算每个分段文档的向量(文档向量),然后把分段文档和文档向量同时存入向量数据库。从向量数据库中搜索文档时...
- 融合贝叶斯生存模型与Transformer注意力的客户重参与策略优化
-
本文提出了一个集成三种核心技术的下一代智能优惠券分发系统:基于贝叶斯生存模型的重购概率预测、采用注意力机制的Transformer利润预测模型,以及用于策略持续优化的Dyna-Q强化学习代理。该系统构...
- 用Deepseek编写代码计算今天大乐透开奖号码
-
以下是一个基于Python的示例代码,用于分析大乐透历史数据并生成可能的号码组合。请务必注意:这仅是统计学模拟,无法真正预测开奖结果,所有结果均为随机性参考。代码实现步骤1.数据准备(模拟数据)假设...
- 拆解特斯拉L2家用充电桩:技术细节太多了
-
本文是对第三代特斯拉家用充电桩(L2级)的拆解分析报告。深入探究该充电桩的内部结构、设计特点、性能参数等内容。产品概述设备为第三代特斯拉家用充电桩,属于Level2充电器,是特斯拉推出的家用充电设备...
- 《光环5》2月更新“战锤风暴”正式推送“枪林弹雨”模式即将到来
-
今天(2月25日)微软和343工作室正式向Xboxone玩家推送了《光环5》的2月更新补丁“战锤风暴HammerStorm”。本次更新包括了1张全新Arena竞技场地图Torque;3个全新游戏模式...
- Spring Boot(十一)Redis集成从Docker安装到分布式Session共享
-
一、简介Redis是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API,Redis也是技术领域使用最为广泛的存储中间件,它是「...
- Mac 基于HTTP方式访问下载共享文件,配置共享服务器
-
方法一:使用Python的SimpleHTTPServer进行局域网文件共享Mac自带Python,所以不需要安装其他软件,一条命令即可1):进入需要共享的文件夹,如Public文件夹cd/Us...
- 移动端性能专项测试之 CPU(移动端cpu天梯图2020百度贴吧)
-
指标背景很多场景下我们去使用App,可能会碰到手机会出现发热发烫的现象。这是因为CPU使用率过高、CPU过于繁忙,会使得整个系统无法响应用户,整体性能降低,用户体验变得相当差,也容易引起AN...
- 如何三天学会Phyton?这篇文章教你快速编程入门
-
Phyton作为一门常用的语言在很多领域都有很应用,很多人都想学习这门语言,那么我们就开始从头学习这门语言吧!首先你需要在官网下载你的Phyton的编程工具,也就是下载你的解释器!登录Phyton官网...
- 学习Python第一天 ---Hello World
-
引言人生苦短,请用Python(3.+)越来越多的情况下使用Python语言进行"代码粘合"和"数据分析"变得非常方便,而且Python在"爬虫"...
- mysql的MVCC多版本并发控制机制(mysql并发情况下怎么解决)
-
认识MVCCMVCC是英文Multi-VersionConcurrencyControl多版本并发控制的首字母简拼。在上文MYSQL事务隔离级别中,我们已经知道,在可重复读的级别下,不管其他事...
- 爆炸,MySQL9.0大版本发布,我严重怀疑,它是不...
-
MySQL在本月发布了9.0大版本,作为MySQL的忠实粉丝,简单说下这次大版本更新。1.企业版,支持JS存储程序(JavaScriptstoredprograms)了。例如,可以像这样定一个函...
- 一周热门
- 最近发表
- 标签列表
-
- ps图案在哪里 (33)
- super().__init__ (33)
- python 获取日期 (34)
- 0xa (36)
- super().__init__()详解 (33)
- python安装包在哪里找 (33)
- linux查看python版本信息 (35)
- python怎么改成中文 (35)
- php文件怎么在浏览器运行 (33)
- eval在python中的意思 (33)
- python安装opencv库 (35)
- python div (34)
- sticky css (33)
- python中random.randint()函数 (34)
- python去掉字符串中的指定字符 (33)
- python入门经典100题 (34)
- anaconda安装路径 (34)
- yield和return的区别 (33)
- 1到10的阶乘之和是多少 (35)
- python安装sklearn库 (33)
- dom和bom区别 (33)
- js 替换指定位置的字符 (33)
- python判断元素是否存在 (33)
- sorted key (33)
- shutil.copy() (33)