MySQL MVCC多版本并发控制(脏读和不可重复读解决原理)

article/2025/10/19 6:32:25

文章目录

    • 一、MVCC概念
    • 二、MVCC应用于已提交读隔离级别
      • 1. 解决脏读
      • 2. 无法解决不可重复读
      • 3. 无法解决幻读
    • 三、MVCC应用于可重复读隔离级别
      • 1. 解决脏读
      • 2. 解决不可重复读
      • 3. 理解 可重复读隔离级别,只生成一次数据快照
      • 4. 理解 可重复读隔离级别,只能部分解决幻读

一、MVCC概念

MVCC是多版本并发控制(Multi-Version Concurrency Control),是MySQL中基于乐观锁理论实现隔离级别的方式,用于实现已提交读和可重复读隔离级别,也经常称为多版本数据库。MVCC机制会生成一个数据请求时间点的一致性数据快照 (Snapshot), 并用这个快照来提供一定级别 (语句级或事务级) 的一致性读取。从用户的角度来看,好象是数据库可以提供同一数据的多个版本(系统版本号和事务版本号)

MVCC多版本并发控制中,读操作可以分为两类:

  • 快照读(非锁定读):读的是记录的可见版本,不用加锁。如 select做的都是快照读,会把已经commit的数据(即整表数据)生成一个快照(这就可以防止不可重复读)
  • 当前读:读取的是记录的最新版本,返回当前读的记录,并且对数据加锁。如 insert,delete,update,select…lock in share mode/for update这些操作,都是读的是最新的数据

MVCC:每一行记录实际上有多个版本,每个版本的记录除了数据本身之外,增加了其它字段(DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR)

已提交读隔离级别:每个语句执行前都会重新生成一个 Read View,快照中只包含已commit的数据
可重复读隔离级别:启动事务时生成一个 Read View,然后整个事务期间都在用这个 Read View,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,所以事务过程中每次查询的数据都是一样的

什么叫事务启动呢?

  • 执行了 begin/start transaction 命令后,并不代表事务启动了。只有在执行这个命令后,执行了增删查改操作的 SQL 语句,才是事务真正启动的时机
  • 执行了 start transaction with consistent snapshot 命令,就会马上启动事务

快照内容读取原则:

  1. 版本未commit,无法读取生成快照
  2. 版本已commit,但是在快照创建后提交的,无法读取
  3. 版本已commit,但是在快照创建前提交的,可以读取
  4. 当前事务做的修改,是需要重新生成快照的。读取的是最新版本,并且对数据加锁,阻塞其他操作事务修改记录。核心逻辑就是判断版本链中的哪个版本是当前事务可见可处理的

"数据快照"中并不是数据,存储的是一些事务id

在这里插入图片描述

Read View 有四个重要的字段:

  • creator_trx_id :指的是创建该 Read View 的事务的事务 id
  • m_ids :指的是在创建 Read View 时,当前数据库中「活跃事务」的事务 id 列表,注意是一个列表,“活跃事务”指的就是,启动了但还没提交的事务。重新生成数据快照m_ids可能会有更新,不重新生成数据快照m_ids就不会更新
  • min_trx_id :指的是在创建 Read View 时,当前数据库中「活跃事务」中事务 id 最小的事务,也就是 m_ids 的最小值
  • max_trx_id :这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值,也就是所有已提交的和未提交的事务中最大的事务 id 值 + 1

Innodb如何判断某条记录是否对当前事务可见呢?一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:

  • 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View 前已经提交的事务生成的,所以该版本的记录对当前事务可见
  • 如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View 后才启动的事务生成的,所以该版本的记录对当前事务不可见
  • 如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间,需要判断 trx_id 是否在 m_ids 列表中:
    • 如果记录的 trx_id 在 m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见
    • 如果记录的 trx_id 不在 m_ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见

这种通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制)

在已提交读隔离级别下,每次查询都会重新生成数据快照,若其他事务已经提交了,当前事务再次查询时重新生成的数据快照中的m_ids、min_trx_id、max_trx_id可能会发生改变,这样对比每条记录的trx_id后,可见性就会发生改变

在可重复读隔离级别下,每次查询都使用第一次生成的数据快照,

二、MVCC应用于已提交读隔离级别

1. 解决脏读

先设置隔离级别为已提交读并开启事务,已提交读解决了脏读,未解决可重复读和幻读

在这里插入图片描述

在这里插入图片描述
这样通过快照读,MVCC就解决了脏读

不管是已提交读还是可重复读,只要我们select的时候,就会产生一个数据快照,相当于给当前的数据拍个照片,以后去查询,都是查询快照上的数据(除非有新的数据被commit)。已提交读隔离级别采用非锁定读,非锁定读是在快照上的读取。

在已提交读隔离级别,每一次select都会产生一个新的数据快照,当事务1进行更改的时候,事务2又去select,重新产生数据快照(有可能和前面的快照相同),然而产生新的数据快照的前提是新的数据已经被事务正确commit,prepare状态的数据不会出现在快照中

数据有2种状态:prepare(未提交时)和commit(已提交)

事务2第二次select的时候,由于事务1并没有commit新的数据(数据处于prepare状态),当又一次产生数据快照时,产生的数据快照还是undo log回滚日志的链表指向的旧数据,这就解决了脏读问题

然而,在已提交读隔离级别依然会发生不可重复读的现象(两次查询,得到的数据内容不一样,属于正确读取的范围)

在这里插入图片描述

2. 无法解决不可重复读

因为每一次select都会重新产生1次数据快照,其他事务update后commit,新的数据已经符合生成快照的要求了,于是再次select的时候新commit的数据也会出现在新生成的快照中,发生了不可重复读

3. 无法解决幻读

在这里插入图片描述

在这里插入图片描述
和出现不可重复读现象的原因相同,由于新commit的数据符合生成快照的要求,再次select的时候新commit的数据也会出现在新生成的快照中,自然就出现了幻读

三、MVCC应用于可重复读隔离级别

1. 解决脏读

事务第一次select就产生数据快照,而且只产生这一次快照,select时都是直接用老的数据快照,所以可以解决脏读

2. 解决不可重复读

因为事务第一次select就产生数据快照,而且只产生这一次快照

设置可重复读隔离级别,并2个开启事务

在这里插入图片描述
事务2 select,生成数据快照,在可重复读隔离级别下,以后再select都不会再生成快照

在这里插入图片描述
生成的快照如下:
在这里插入图片描述
事务1进行update,然后commit

在这里插入图片描述
我们update以后,表格就变成了这样:

在这里插入图片描述
我们事务2再次select id=12的数据,这时候就是在事务2第一次select生成的快照上查数据了

在这里插入图片描述
这就解决了不可重复读!!!

3. 理解 可重复读隔离级别,只生成一次数据快照

再举一个例子理解:在可重复读隔离级别,只生成一次数据快照

在这里插入图片描述
在这里插入图片描述
由于事务1已经commit了,新的数据不再是prepare状态,已经符合了生成快照的条件。当事务2再select(快照读)的时候,这条age=22的数据自然就被查到了

4. 理解 可重复读隔离级别,只能部分解决幻读

先查看表数据
在这里插入图片描述
回滚并重启事务
在这里插入图片描述
在这里插入图片描述
事务2生成的快照如下:

在这里插入图片描述

事务2第一次select是两条数据,事务1 insert之后,事务2再次select依然是两条,看似解决了幻读,其实只是部分解决(并不能完全解决幻读)

那我们看一下为什么是部分解决幻读

事务1 insert然后commit后,表格的数据应该是这样的

在这里插入图片描述
此时事务2 update
在这里插入图片描述

可以看见,update找到了id=24的数据,这就证明update做的是当前读(读最新的commit状态的数据),而不是快照读,因为快照上根本就没有id=24的数据

在这里插入图片描述
其中1000是事务1的ID,2000的事务2的ID

由于每个事务可以看见自己修改、更新的数据,当事务2再次select的时候,就可以看见id=24的数据了,这就发生了幻读(主要因为insert,delete,update,select…lock in share mode/for update这些操作,是当前读)

在这里插入图片描述

未提交读已提交读可重复读串行化
/MVCCMVCC + 临键锁临键锁
脏读、不可重复读、幻读不可重复读、幻读幻读/

http://chatgpt.dhexx.cn/article/9iicoDph.shtml

相关文章

mysql防止不可重复读_mysql怎么解决不可重复读

mysql解决不可重复读的方法:采用了mvcc多版本并发控制,mvcc是利用在每条数据后面加了隐藏的两列,即创建版本号和删除版本号,每个事务在开始的时候都会有一个递增的版本号。 【相关学习推荐: mysql解决不可重复读的方法…

MySQL事务之不可重复读问题

版权声明:本文为 小异常 原创文章,非商用自由转载-保持署名-注明出处,谢谢! 本文网址:https://blog.csdn.net/sun8112133/article/details/89739475 文章目录 一、不可重复读概述1、事例2、分析 二、演示不可重复读1、…

数据库脏读、幻读、不可重复读与四种隔离级别的理解

前言 本章着重点是介绍:出现脏读、幻读、不可重复读的原因,以及设定四种隔离级别如何解决这些现象,结合图例让读者阅读得更加通透 一、什么是事务? 所谓事务是用户定义的一个数据库序列,这些操作要么全做&#xff0…

不可重复读和幻读 重

如有侵权请联系本人删除 首先了解acid与mysql MySQL事务之ACID实现原理(全方位解读) 深入学习MySQL事务:ACID特性的实现原理 二者很相似,不可重复读指的是对同一条记录(可以理解为对同一行)前后两次的读取…

灰色关联法 —— matlab

目录 1.简介 2.算法详解 2.1 数据标准化 2.2 计算灰色相关系数 2.3 计算灰色关联度系数 3.实例分析 3.1 读取数据 3.2 数据标准化 3.3 绘制 x1,x4,x5,x6,x7 的折线图 3.4 计算灰色相关系数 完整代码 1.简介 对于两个系统之间的因素,其随时间或不同对象而…

如何利用python实现灰色关联分析?

1.灰色关联分析简介 灰色系统这个概念是相对于白色系统和黑色系统而言的。从控制论的知识里,颜色一般代表对于一个系统我们已知信息的多少,白色代表信息量充足,黑色代表我们其中的构造并不清楚的系统,而灰色介于两者之间&#xf…

灰色关联分析法详解及python实践

1. 关于灰色关联分析 1.1. 什么是灰色关联分析 灰色关联分析是指对一个系统发展变化态势的定量描述和比较的方法,其基本思想是通过确定参考数据列和若干个比较数据列的几何形状相似程度来判断其联系是否紧密,它反映了曲线间的关联程度。 在系统发展过…

python灰色关联度分析_灰色关联分析法 python

广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 本文2290字,预计阅读需10分钟; 关联分析(association analysis)主要用于发现隐藏在大型数据集中的有意义的联系,它起源于商品销售领域,“啤酒与尿布”的故…

灰色关联分析应用

参考学习b站:数学建模学习交流 感觉这篇博客写的很好:灰色关联分析Matlab代码实现 看完后自己尝试运行的一下,发现和自己本地的matlab代码运行结果不同,就把其中细节写到这篇博客里了 运行上述博客应用2的代码,将更…

灰色关联案例与代码

案例 求他们之间的关联度,并且大小排序。 代码 写了注释,自行查看,换了案例,你只需要修改这一部分即可: 因为读取不一样了嘛,你总要改改数字。 clc; close; clear all; xxlsread(gray_data1.xlsx); xx(:,2:end);%所…

灰色关联法 —— python

目录 1.简介 2.算法详解 2.1 数据标准化 2.2 计算灰色相关系数 2.3 计算灰色关联度系数 3.实例分析 3.1 读取数据 3.2 数据标准化 3.3 绘制 x1,x4,x5,x6,x7 的折线图 3.4 计算灰色相关系数 完整代码 1.简介 对于两个系统之间的因素,其随时间或不同对象而变…

数学建模之灰色关联分析(GRA)

本文参考的是司守奎,孙兆亮主编的数学建模算法与应用(第二版) 灰色关联分析不仅能够用做关联分析,也能够用于评价。 其具体分析步骤如下: 第一步,需要确定评价对象和参考数列。 评价对象一般指的就是待分…

【DOM】DOM操作之如何添加、删除、替换元素_04

目录 一. 添加删除替换元素 1. 添加新元素: 3步: (1). 创建一个新的空元素对象 (2). 为新元素设置必要的属性: 新元素.属性名"属性值" (3). 将新元素添加到DOM树 2. 示例: 动态创建a元素和input元素 3. 示例: 动态生成表格内容 4. 优化 5. 删除元素: 父元素.re…

DOM操作_获取元素

概述 DOM (Document objectModal) :文档对象模型。 DOM;是浏览器提供的(浏览器特有),专[ ]用来操作网页内容的一些JS对象。 目的:让我们可以使用Js/TS代码来操作页面(HTML) 内容,让页面“动”起来,从而实现Web开发。 HTML:超文本标记语言…

DOM操作class属性的方法

新增标签对象.classList.add(新增属性值) 删除 标签对象.classList.remove(删除属性值) 替换标签对象.classList.replace(原始 , 新的) 切换标签对象.classList.toggle(切换属性值)有执行删除,没有执行新增 <body><div class "box box2 "></div&…

js DOM操作自定义属性

自定义属性&#xff1a;在日常开发中&#xff0c;html的内置属性已经无法满足程序员的日常开发&#xff0c;所以需要我们自己定义属性&#xff0c;H5给我们新增了自定义属性&#xff0c;为了防止自定义属性和内置属性引起歧义&#xff0c;所以H5规定自定义属性以data-开头定义。…

JavaScript之DOM操作获取元素、事件、操作元素、节点操作

什么是 DOM&#xff1f; 文档对象模型&#xff08;Document Object Model&#xff0c;简称 DOM&#xff09;&#xff0c;是 W3C 组织推荐的处理可扩展标记语言&#xff08;HTML或者XML&#xff09;的标准编程接口。W3C 已经定义了一系列的 DOM 接口&#xff0c;通过这些 DOM 接…

DOM 基础操作

文章目录 前言一、DOM 简介1.1 什么是 DOM 二、获取元素2.1 如何获取页面元素2.2 根据 ID 获取2.3 根据标签名获取2.4 通过 HTML5 新增的方法获取2.5 获取特殊元素&#xff08;body&#xff0c;html&#xff09; 三、事件基础3.1事件三要素3.2 执行事件的步骤3.3常见的鼠标事件…

[js]DOM操作

DOM也就是文档对象模型&#xff1a;document object model 主要用来动态操作HTML的元素节点&#xff0c;dom操作文档是居于DOM树为根据操作的。DOM树为html整体结构框架&#xff0c;其中有各种嵌套标签&#xff0c;最典型的就是以body为主容器&#xff0c;在body内部设置各种元…

DOM操作总结  (。♥ᴗ♥。) 哇!!

知识点 DOM 本质DOM 节点操作DOM 结构操作DOM 性能 前言 各种框架层出不穷&#xff0c;但DOM操作一直都会是前端工程师的基础&#xff0c;必备知识。 只会Vue和React等框架&#xff0c;而不懂DOM操作的前端程序员们。。。 DOM的本质&#xff1f; Do you know&#xff1f; 首…