【MySQL】MVCC的实现原理

【MySQL】MVCC的实现原理

  • MVCC简介
  • 事务的隔离级别
    • 读未提交(Read Uncommitted)
      • 概念
      • 分析
    • 读已提交(Read Committed)
      • 概念
      • 分析
      • 结论
    • 可重复读(Repeatable Read)
      • 概念
      • 分析
      • 结论
    • 串行化(Serializable )
      • 概念
      • 结论
  • MVCC实现原理
    • 隐藏字段
    • Undo Log 版本链
    • Read View
    • 判断方法
    • 判断规则
  • 扩展1:当前读和快照读
  • 扩展2:数据库事务的四大特性
  • 小结

MVCC简介

MVCC(Multi-Version Concurrency Control)是一种并发控制机制,用于解决数据库并发访问中,数据一致性问题。它通过在读写操作期间保存同一个数据的多个数据版本,以提供并发事务间的隔离性,从而避免了传统的锁机制所带来的资源争用和阻塞问题。本质上就是一个空间换时间的逻辑。

MVCC是关于事务隔离级别的无锁的实现方式。它用于提高事务的并发性能。

因此,首先来简单介绍下什么是事务的隔离级别,已经了解这一内容的小伙伴可以直接跳过这部分。

事务的隔离级别

并发事务会出现如下三个问题:

  1. 脏读:一个事务A读取到了另一个事务B修改的数据之后,然后这个事务B又进行了回滚操作,从而导致第一个事务A读取的数据是错误的。
  2. 不可重复读:一个事务A两次查询得到的结果不同,因为在两次查询中间,有另一个事务B把数据修改了。
  3. 幻读:一个事务A两次查询中得到的结果集不同,因为在两次查询中另一个事务B又新增了一部分数据。

为了解决并发事务存在的脏读、不可重复读、幻读等问题,数据库大叔设计了四种隔离级别,下面我们用一个例子来分别看看这四个隔离级别。

建表语句:

CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(30) DEFAULT NULL,`age` tinyint(4) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

创建一条记录:

mysql> SELECT * FROM user;
+----+-----------------+------+
| id | name            | age  |
+----+-----------------+------+
|  1 | 古时的风筝        |    1 |
+----+-----------------+------+

读未提交(Read Uncommitted)

概念

读未提交隔离级别,限制了两个数据不能同时修改,但任何事务对数据的修改都会第一时间暴露给其他事务,即使事务还没有提交。

分析

下面来做个简单实验验证一下,首先设置全局隔离级别为读未提交。

set global transaction isolation level read uncommitted;

设置完成后,只对之后新起的 session 才起作用,对已经启动 session 无效。如果用 shell 客户端那就要重新连接 MySQL,如果用 Navicat 那就要创建新的查询窗口。

启动两个事务,分别为事务A和事务B,在事务A中使用 update 语句,修改 age 的值为10,初始是1 ,在执行完 update 语句之后,在事务B中查询 user 表,会看到 age 的值已经是 10 了,这时候事务A还没有提交,而此时事务B有可能拿着已经修改过的 age=10 去进行其他操作了。在事务B进行操作的过程中,很有可能事务A由于某些原因,进行了事务回滚操作,那其实事务B得到的就是脏数据了,拿着脏数据去进行其他的计算,那结果肯定也是有问题的。

顺着时间轴往表示两事务中操作的执行顺序,重点看图中 age 字段的值。
在这里插入图片描述
读未提交,其实就是可以读到其他事务未提交的数据,但没有办法保证你读到的数据最终一定是提交后的数据,如果中间发生回滚,那就会出现脏数据问题,读未提交没办法解决脏数据问题。更别提不可重复读和幻读了,想都不要想。

结论:这级别的事务隔离有脏读、重复读、幻读的问题,基本不会使用。

读已提交(Read Committed)

概念

既然读未提交没办法解决脏数据问题,那么就有了读提交。读提交就是一个事务只能读到其他事务已经提交过的数据,也就是其他事务调用 commit 命令之后的数据。那脏数据问题迎刃而解了。
读提交事务隔离级别是大多数流行数据库的默认事务隔离界别,比如 Oracle,但是不是 MySQL 的默认隔离界别。

分析

我们继续来做一下验证,首先把事务隔离级别改为读提交级别。

set global transaction isolation level read committed;

之后需要重新打开新的 session 窗口,也就是新的 shell 窗口才可以。

同样开启事务A和事务B两个事务,在事务A中使用 update 语句将 id=1 的记录行 age 字段改为 10。此时,在事务B中使用 select 语句进行查询,我们发现在事务A提交之前,事务B中查询到的记录 age 一直是1,直到事务A提交,此时在事务B中 select 查询,发现 age 的值已经是 10 了。

这就出现了一个问题,在同一事务中(本例中的事务B),事务的不同时刻同样的查询条件,查询出来的记录内容是不一样的,事务A的提交影响了事务B的查询结果,这就是不可重复读,也就是读提交隔离级别。
在这里插入图片描述
每个 select 语句都有自己的一份快照,而不是一个事务一份,所以在不同的时刻,查询出来的数据可能是不一致的。

结论

读已提交的隔离级别解决了脏读的问题,但还是解决不了不可重复读、幻读问题。

可重复读(Repeatable Read)

概念

可重复是对比不可重复而言的,上面说不可重复读是指同一事物不同时刻读到的数据值可能不一致。而可重复读是指,事务不会读到其他事务对已有数据的修改,及时其他事务已提交,也就是说,事务开始时读到的已有数据是什么,在事务提交前的任意时刻,这些数据的值都是一样的。但是,对于其他事务新插入的数据是可以读到的,这也就引发了幻读问题。

分析

同样的,需改全局隔离级别为可重复读级别。

set global transaction isolation level repeatable read;

在这个隔离级别下,启动两个事务,两个事务同时开启。

首先看一下可重复读的效果,事务A启动后修改了数据,并且在事务B之前提交,事务B在事务开始和事务A提交之后两个时间节点都读取的数据相同,已经可以看出可重复读的效果。
在这里插入图片描述
可重复读做到了,这只是针对已有行的更改操作有效,但是对于新插入的行记录,就没这么幸运了,幻读就这么产生了。我们看一下这个过程:

事务A开始后,执行 update 操作,将 age = 1 的记录的 name 改为“风筝2号”;
事务B开始后,在事务执行完 update 后,执行 insert 操作,插入记录 age =1,name = 古时的风筝,这和事务A修改的那条记录值相同,然后提交。

事务B提交后,事务A中执行 select,查询 age=1 的数据,这时,会发现多了一行,并且发现还有一条 name = 古时的风筝,age = 1 的记录,这其实就是事务B刚刚插入的,这就是幻读。
在这里插入图片描述
需要注意的是,当你在 MySQL 中实际测试幻读的时候,并不会出现上图的结果,因为幻读并不会发生,MySQL 的可重复读隔离级别其实解决了幻读问题,它就是间隙锁,感兴趣的小伙伴可以去了解一下,这里就不介绍了。

结论

可重复读隔离级别,解决了赃读、不可重复读和幻读的问题,并且没有用到MySql默认隔离级别

串行化(Serializable )

概念

事务最高的隔离级别,在该级别下,所有事务都是进行串行化顺序执行的。

结论

串行化隔离级别,可以避免脏读、不可重复读与幻读所有并发问题。但是这种事务隔离级别下,事务执行很耗性能。

小结: 读已提交(RC)可重复读(RR) 的隔离问题就是通过MVCC来实现的。

MVCC实现原理

MVCC 实现的三个关键点:

  • 隐藏字段
  • Undo Log 版本链
  • Read View(读视图或者叫一致性视图)

隐藏字段

对于InnoDB存储引擎,每一行记录除了我们的原数据列之外,还有两个隐藏列trx_idroll_pointer,如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列row_id

列名是否必须描述
row_id单调递增的行ID,不是必需的,占用6个字节
trx_id记录操作该数据事务的事务ID
roll_pointer这个隐藏字段相当于一个指针,指向回滚的undo日志

Undo Log 版本链

Undo Log 版本链是 InnoDB 存储引擎中一种重要的数据结构,用于记录数据项的历史修改信息。Undo Log 版本链由多个 Undo Log 记录组成。Undo Log 版本链通过回滚指针将多个 Undo Log 记录连接在一起,形成一个链表结构。每个数据项都维护着自己的 Undo Log 版本链,用于记录该数据项的历史修改信息。主要作用是实现事务的回滚和数据恢复。
在这里插入图片描述

Read View

Read View(读视图)用于管理事务之间数据可见性的一种机制。Read View 在特定时刻为事务创建的一个快照,该快照包含了在该时刻所有未提交事务的事务标识符,以及其他一些辅助信息。(这也就是后面我要和大家说的快照读
在 Read View 中包含了以下 4 个主要的字段:

  • m_ids:当前活跃的事务id集合。
  • min_trx_id:最小活跃事务id。
  • max_trx_id:预分配的事务id,当前最大事务id+1。
  • creator_trx_id:ReadView 创建者的事务id,即当前事务id。

解释:

活跃的事务id:指的是已经创建,但未commit的事务,即处理中的事务id。

RC 级别中,每次快照读都会生成一个全新的 Read View,而 RR 级别中同一个事务会复用一个 Read View。
有了 Read View 和 Undo Log 版本链之后,并发事务在查询数据时就知道要读取哪些数据了。

判断方法

判断方法是根据 Read View 中的 4 个重要字段,先去 Undo Log 版本链中最新的数据行进行比对,如果满足下面 Read View 的判断条件,则返回当前行的数据,如果不满足则继续查找 Undo Log版本链的下一行数据,直到找到满足的条件的数据为止,如果查询完没有满足条件的数据,则返回 NULL。

判断规则

在这里插入图片描述

  • trx_id==creator_trx_id:先将 Undo Log版本链最新数据行中的 trx_id 和 ReadView 中的 creator_trx_id 进行对比,如果他们两个值相同,则说明是在同一个事务中执行,那么直接返回当前 Undo Log版本链的这个数据行即可,如果不相等,则继续下面流程。
  • trx_id<min_trx_id:如果 trx_id 小于 min_trx_id,则说明在执行查询时,其他事务已经提交此行数据了,说明此行数据对所有事务可见,那么直接返回此行数据即可,如果大于等于,则继续下面流程。
  • trx_id>max_trx_id:如果 trx_id 如果大于等于 max_trx_id,则说明当前事务创建之后,才有的当前行数据,那么当前行数据不可见,继续执行后续流程。
  • min_trx_id<=trx_id<max_trx_id:trx_id 在 min_trx_id 和 max_trx_id 之间还分为以下两种情况:
    • trx_id 在 m_ids 中:说明事务尚未执行完,该行数据不可被访问。
    • trx_id 未在 m_ids 中:说明事务已经执行完,可以返回该行数据。

对于删除的情况,会将版本链的最新数据复制一份,然后将trx_id修改成删除操作的trx_id,同时会在记录的头信息的标记位(delete_flag)上设置true,用来表示已经删除,在查询时,如果对应记录的delete_flag为true,则表示已经被删除,就不会返回数据。

以上判断规则从 Undo Log版本链最新的行数据,逐行对比,直到找到匹配的数据,否则查询完未匹配上,则返回 NULL。

扩展1:当前读和快照读

快照读:是指在一个事务中,读取的数据版本是在事务开始时已经存在的数据版本,而不是最新的数据版本。这种读取方式提供了事务在执行期间看到的数据视图的一致性,select 查询就是快照读。

当前读:是指在事务中读取最新的数据版本,以下几种操作都是快照读:

  1. select lock in share mode(共享锁)
  2. select for update(排他锁)
  3. update(排他锁)
  4. insert(排他锁)
  5. delete(排他锁)

扩展2:数据库事务的四大特性

  1. A(Atomicity):原子性
    概念:原子性是指事务中的所有操作要么全部成功,要么全部失败,回滚到事务开始之前的状态。例如,一个银行转账操作,要么钱成功从A账户转到B账户,要么转账操作完全失败,A账户和B账户的余额都不发生变化。
    实现:
    • undo log
  2. C(Consistency):一致性
    概念:一致性是指事务必须将数据库从一个一致性状态转换为另一个一致性状态。例如,一个银行转账操作,必须保证A账户的余额减少的金额等于B账户的余额增加的金额。
    实现:
    • 原子性、隔离性和持久性共同实现
  3. I(Isolation):隔离性
    概念:隔离性是指多个并发执行的事务彼此互不影响,每个事务的执行结果就像是在数据库中单独执行一样。例如,两个用户同时向同一个账户转账,不会出现转账金额丢失或者账户余额错误的情况。
    实现:
    • 锁机制(当前读)
    • MVCC(快照读)
  4. D(Durability):持久性
    概念:持久性是指一旦事务提交,其对数据库所做的修改就将永久保存,即使数据库发生故障,也不会丢失。例如,一个银行转账操作,一旦成功提交,即使数据库服务器宕机,转账操作也不会回滚。
    实现:
    • redolog

undo log 和 redo log 是相互协作的,共同保证了事务的ACID特性。

小结

  • MVCC就是为快照读而生的,维护不同的快照版本,使得不同事务的读-写操作不会冲突,实现多版本并发控制,从而提高性能。

  • MVCC 主要应用于 InnoDB 引擎中的 RC 事务隔离级别和 RR 隔离级别,其中 RC 隔离级别每次快照读都会生成一个新的 Read View,而 RR 隔离级别只在第一次快照读时生成 Read View,之后会复用 Read View,从而解决了(部分)幻读问题。

  • MVCC是一种用于解决数据库并发问题的乐观锁技术,多版本并发控制通过保存数据在某个时间点的快照来实现。换句话说,读操作不会阻塞写操作,写操作也不会阻塞读操作,以此来提高数据库性能。在每次对数据的操作,都在undo log版本链中进行。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WebSocket 深入浅出

WebSocket 深入浅出 1. WebSocket 是什么2. WebSocket 建立连接通信的过程3. WebSocket 和http的联系与区别4. WebSocket 的使用场景及限制 1. WebSocket 是什么 定义&#xff1a;WebSocket 是一种网络通信协议&#xff0c;它允许在单个TCP连接上进行全双工通信。是HTML5规范提…

Scala 多版本下载指南

Scala&#xff0c;这一功能丰富的编程语言&#xff0c;结合了面向对象和函数式编程的精华&#xff0c;为开发者提供了强大的工具来构建高效、可扩展的应用程序。随着Scala社区的不断壮大和技术的演进&#xff0c;多个版本的Scala被广泛应用于不同的项目与场景中。本文旨在为您提…

可重构柔性装配产线:AI边缘控制技术的崭新探索

在信息化和智能化浪潮的推动下&#xff0c;制造业正面临着前所未有的转型升级挑战。其中&#xff0c;可重构柔性装配产线以其独特的AI边缘控制技术&#xff0c;为制造业的智能化转型提供了新的解决方案。 可重构柔性装配产线是基于AI工业控制与决策平台打造的智能化生产系统。…

2024五一数学建模竞赛(五一赛)选题建议+初步分析

提示&#xff1a;DS C君认为的难度&#xff1a;B>A>C&#xff0c;开放度&#xff1a;AB<C。 以下为A-C题选题建议及初步分析&#xff1a; A题&#xff1a;钢板最优切割路径问题 l 难度评估&#xff1a;中等难度。涉及数学建模和优化算法&#xff0c;需要设计最优的…

STM32 工程移植 LVGL:一步一步完成

STM32 工程移植 LVGL&#xff1a;一步一步完成 LVGL&#xff0c;作为一款强大且灵活的开源图形库&#xff0c;专为嵌入式系统GUI设计而生&#xff0c;极大地简化了开发者在创建美观用户界面时的工作。作为一名初学者&#xff0c;小编正逐步深入探索LVGL的奥秘&#xff0c;并决…

3.C++动态内存管理(超全)

目录 1 .C/C 内存分布 2. C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free 3. C内存管理方式 3.1 new/delete操作内置类型 3.2 new和delete操作自定义类型 3.3 operator new函数 3.4 定位new表达式(placement-new) &#xff08;了解&#xff09; 4. 常…

java-springmvc 01 补充 javaweb 三大组件Servlet,Filter、Listener(源码都是tomcat8.5项目中的)

01.JavaWeb三大组件指的是&#xff1a;Servlet、Filter、Listener,三者提供不同的功能 这三个在springmvc 运用很多 Servlet 01.Servlet接口&#xff1a; public interface Servlet {/*** 初始化方法* 实例化servlet之后&#xff0c;该方法仅调用一次 * init方法必须执行完…

SpringCloud(微服务介绍,远程调用RestTemplate,注册中心Nacos,负载均衡Ribbon,环境隔离,进程和线程的区别)【详解】

目录 一、微服务介绍 1. 系统架构的演变 1 单体架构 2 分布式服务 3 微服务 2. SpringCloud介绍 SpringCloud简介 SpringCloud版本 3. 小结 二、远程调用RestTemplate【理解】 1. 服务拆分 1 服务拆分原则 2 服务拆分示例 1) 创建父工程 2) 准备用户服务 1. 用户…

Kubernetes TDengine 系列|安装 TDengine 的 Grafana 插件|Grafana监控TDengine数据

为了让Grafana 能够监控到TDengine 数据&#xff0c;快速集成搭建数据监测报警系统&#xff0c;所以直接安装TDengine 插件。 目录 一、安装 TDengine 的 Grafana 插件1、下载TDengine grafana插件2、解压到指定目录3、配置未签名插件 二、配置数据源&#xff0c;简单查询TDen…

python的输入输出(爽文,备忘,查询,友好)

Python中的输入输出主要涉及到输入函数和输出函数。 输出函数&#xff1a;print() print() 函数用于将信息输出到屏幕上。它可以输出字符串、变量的值&#xff0c;以及其他各种数据类型。 name "Alice" age 30 print("姓名:", name, "年龄:&quo…

气象数据nc数据矢量化处理解析及可视化

气象数据可视化是将气象学领域中复杂的数据集转化为图形或图像的过程&#xff0c;以直观展示天气现象、气候模式、趋势和预报结果。气象数据的可视化技术广泛应用于科学研究、气象预报、航空、航海、农业生产、灾害预警系统、城市规划、公众服务等领域。以下是一些关键的气象数…

mac虚拟机软件哪个好 mac虚拟机怎么安装Windows 苹果Mac电脑上受欢迎的主流虚拟机PK Parallels Desktop和VM

什么是苹果虚拟机&#xff1f; 苹果虚拟机是一种软件工具&#xff0c;它允许在非苹果硬件上运行苹果操作系统&#xff08;如ios&#xff09;。通过使用虚拟机&#xff0c;您可以在Windows PC或Linux上体验和使用苹果的操作系统&#xff0c;而无需购买苹果硬件。 如何使用苹果虚…

【智能算法】海象优化算法(WO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2024年&#xff0c;M Han等人受到海象群体自然行为启发&#xff0c;提出了海象优化算法&#xff08;Walrus Optimizer, WO&#xff09;。 2.算法原理 2.1算法思想 WO灵感来自海象通过接收关键信号…

设计模式-01 设计模式单例模式

设计模式-01 设计模式单例模式 目录 设计模式-01 设计模式单例模式 1定义 2.内涵 3.使用示例 4.具体代码使用实践 5.注意事项 6.最佳实践 7.总结 1 定义 单例模式是一种设计模式&#xff0c;它确保一个类只能被实例化一次。它通过在类内部创建类的唯一实例并提供一个全…

飞书API(6):使用 pandas 处理数据并写入 MySQL 数据库

一、引入 上一篇了解了飞书 28 种数据类型通过接口读取到的数据结构&#xff0c;本文开始探讨如何将这些数据写入 MySQL 数据库。这个工作流的起点是从 API 获取到的一个完整的数据&#xff0c;终点是写入 MySQL 数据表&#xff0c;表结构和维格表结构类似。在过程中可以有不同…

完美解决AttributeError: module ‘backend_interagg‘ has no attribute ‘FigureCanvas‘

遇到这种错误通常是因为matplotlib的后端配置问题。在某些环境中&#xff0c;尤其是在某些特定的IDE或Jupyter Notebook环境中&#xff0c;可能会因为后端配置不正确而导致错误。错误信息提示 module backend_interagg has no attribute FigureCanvas 意味着当前matplotlib的后…

首页最新 多IP浏览器防关联:如何配置多个独立且稳定的IP地址?

在互联网时代&#xff0c;IP地址的重要性不言而喻。然而&#xff0c;IP关联问题却成为一项令人担忧的隐私和安全挑战。针对这个问题&#xff0c;多IP浏览器是一种解决方案&#xff0c;可以帮助用户单独配置多个独立且稳定的IP地址&#xff0c;有效地防止IP关联。 一、IP关联是…

【Python小练】求斐波那契数列第n个数

题目 输出斐波那契数列第n个数。 分析 首先我们要知道&#xff0c;斐波那契数列&#xff0c;这个数列从第三位开始等于前两个数的和&#xff0c;要知道数列第n个数&#xff08;n>2&#xff09;&#xff0c;就要知道其前两相的值&#xff0c;着就需要用到递归了。来看一下吧…

开源、轻量、易用的服务器实时监控工具:哪吒探针

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 哪吒探针是一个开源、轻量、易用的服务器监控、运维工具&#xff0c;它有以下几个特点&#xff1a; 一键安装&#xff1a;可以一键安装面板与 Agent&#xff0c;并且支持 Linux、Windows、MacOS、OpenWRT…

纯血鸿蒙APP实战开发——发布图片评论

介绍 本示例将通过发布图片评论场景&#xff0c;介绍如何使用startAbilityForResult接口拉起相机拍照&#xff0c;并获取相机返回的数据。 效果图预览 使用说明 通过startAbilityForResult接口拉起相机&#xff0c;拍照后获取图片地址。 实现思路 创建CommentData类&#…