MySQL MVCC多版本并发控制机制原理详解

news2024/11/25 16:37:34

目录

    • 一、前言
    • 二、MVCC解决了什么问题
    • 三、MVCC核心 Undo Log 和 Read View 介绍
      • 3.1、Undo Log(日志版本链)
      • 3.2、Read View(一致性视图)
        • 3.2.1、设计思路
        • 3.2.2、ReadView判断规则
    • 四、数据准备
    • 五、举例探究MVCC机制
      • 5.1、例子执行流程
      • 5.2、关键步骤分析
        • 5.2.1、第6步 #select 1中查询id为1的数据底层处理
        • 5.2.2、第10步 #select 1中查询id为1的数据底层处理
        • 5.2.3、第12步 # Transaction 30中事务内查询id为1的数据底层处理
        • 5.2.4、第13步 # select 2中查询id为1的数据底层处理
        • 5.2.5、第15步 # select 2中查询id为1的数据底层处理
      • 5.3、已提交读的ReadView是如何生成的

一、前言

       MySQL在读已提交可重复读隔离级别下都实现了MVCC机制,MySQL在可重复读隔离级别下如何保证事务较高的隔离性,同样的sql查询语句在一个事务里多次执行查询结果相同,就算其它事务对数据有修改也不会影响当前事务sql语句的查询结果。
       这个隔离性就是靠MVCC(Multi-Version Concurrency Control)机制来保证的,对一行数据的读和写两个操作默认是不会通过加锁互斥来保证隔离性,避免了频繁加锁互斥,而在串行化隔离级别为了保证较高的隔离性是通过将所有操作加锁互斥来实现的。
       MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读,而这个读指的就是快照读, 而非当前读。当前读实际上是一种加锁的操作,是悲观锁的实现。而MVCC本质是采用乐观锁思想的一种方式。

  • 快照读
    快照读又叫一致性读,读取的是快照数据。不加锁的简单的 SELECT 都属于快照读,即不加锁的非阻塞读。
    之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于MVCC,它在很多情况下,避免了加锁操作,降低了开销。既然是基于多版本,那么快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读。

  • 当前读
    当前读读取的是记录的最新版本(最新数据,而不是历史版本的数据),读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。加锁的 SELECT,或者对数据进行增删改都会进行当前读。

事务隔离级别与锁机制,有需要可以跳转MySQL 事务隔离级别与锁机制详解查看。

二、MVCC解决了什么问题

       MVCC 是通过数据行的多个版本管理来实现数据库的并发控制,简单来说它的思想就是保存数据的历史版本,我们可以通过比较版本号决定数据是否显示出来(具体的规则后面会介绍到),读取数据的时候不需要加锁也可以保证事务的隔离效果。

  • 1、读写之间阻塞的问题,通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。

  • 2、降低了死锁的概率。这是因为 MVCC 采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。

  • 3、解决一致性读的问题。一致性读也被称为快照读,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。

三、MVCC核心 Undo Log 和 Read View 介绍

3.1、Undo Log(日志版本链)

       undo日志版本链是指一行数据被多个事务依次修改过后,在每个事务修改完后,MySQL会保留修改前的数据undo回滚日志,并且用两个隐藏字段trx_id和roll_pointer把这些undo日志串联起来形成一个历史记录版本链(见下图)。
在这里插入图片描述

3.2、Read View(一致性视图)

       在可重复读隔离级别,当事务开启,执行任何查询sql时会生成当前事务的一致性视图read-view,该视图在事务结束之前都不会变化(如果是读已提交隔离级别在每次执行查询sql时都会重新生成),InnoDB为每个事务构造了一个数组,用来记录并维护系统当前活跃事务的ID(“活跃”指的就是,启动了但还没提交)。

3.2.1、设计思路
  • 使用读未提交(READ UNCOMMITTED)隔离级别的事务,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了。

  • 使用串行化(SERIALIZABLE)隔离级别的事务,InnoDB规定使用加锁的方式来访问记录。

  • 使用读已提交(READ COMMITTED)可重复读(REPEATABLE READ)隔离级别的事务,都必须保证读到已经提交了的事务修改过的记录。假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,核心问题就是需要判断一下版本链中的哪个版本是当前事务可见的,这是ReadView要解决的主要问题。

这个ReadView中主要包含4个比较重要的内容,分别如下:

  • creator_trx_id,创建这个 Read View 的事务 ID。

    说明:只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务id,否则在一个只读事务中的事务id值都默认为0。

  • trx_ids,表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。

  • up_limit_id,活跃的事务中最小的事务 ID。

  • low_limit_id,表示生成ReadView时系统中应该分配给下一个事务的id值。low_limit_id 是系统最大的事务id值,这里要注意是系统中的事务id,需要区别于正在活跃的事务ID。

注意:low_limit_id并不是trx_ids中的最大值,事务id是递增分配的。比如,现在有id为1, 2,3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,trx_ids就包括1和2,up_limit_id的值就是1,low_limit_id的值就是4。

3.2.2、ReadView判断规则

有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见。

  • 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  • 如果被访问版本的trx_id属性值小于ReadView中的up_limit_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
  • 如果被访问版本的trx_id属性值大于或等于ReadView中的low_limit_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
  • 如果被访问版本的trx_id属性值在ReadView的up_limit_idlow_limit_id之间,那就需要判断一下trx_id属性值是不是在 trx_ids 列表中。
    • 如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问。
    • 如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。

在这里插入图片描述

四、数据准备

先准备好数据用于后面做举例。

# 创建表
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
 `id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
 `name` VARCHAR ( 255 ) DEFAULT NULL,
 `balance` INT ( 11 ) DEFAULT NULL,
 PRIMARY KEY ( `id` ) 
) ENGINE = INNODB COMMENT = '账户表';
# 插入数据
INSERT INTO `account` (`id`,`name`, `balance`) VALUES (1,'Kerwin',1000);
INSERT INTO `account` (`id`,`name`, `balance`) VALUES (2,'Alia',800);
INSERT INTO `account` (`id`,`name`, `balance`) VALUES (3,'Ross',900);

五、举例探究MVCC机制

       这里会先分析可重复读事务隔离级别下的MVCC机制,已提交读流程基本一致除了每次查询时都会生成新的Read View(一致性视图)。

注意:还要明白一点begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个修改操作InnoDB表的语句,事务才真正启动,才会向MySQL申请事务id,MySQL内部是严格按照事务的启动顺序来分配事务id的,并且如果一个事务中只有查询操作是不会生成显式的事务id的。

5.1、例子执行流程

  • 事务id是有序自增的我这里为了便于区分写成了20、30、100。
# Transaction 20# Transaction 30# Transaction 100# select 1# select 2
1begin;begin;begin;begin;begin;
2update account set name=‘Alia20’ where id = 2;
3update account set name=‘Ross30’ where id = 3;
4update account set name=‘Kerwin100’ where id = 1;
5commit;
6select name from account where id = 1; --readview:[20,30], 100 name=‘Kerwin100’
7update account set name=‘Kerwin20’ where id = 1;
8update account set name=‘Kerwin21’ where id = 1;
9commit;
10select name from account where id = 1; --readview:[20,30], 100 name=‘Kerwin100’
11update account set name=‘Kerwin30’ where id = 1;
12select name from account where id = 1; --readview:[30], 100 name=‘Kerwin30’
13select name from account where id = 1; --readview:[30], 100 name=‘Kerwin21’
14commit;
15select name from account where id = 1; --readview:[30], 100 name=‘Kerwin21’;

5.2、关键步骤分析

5.2.1、第6步 #select 1中查询id为1的数据底层处理

       这一步查询出来的name=‘Kerwin100’,但是# Transaction 100和# select 1是同时开启事务的,在第6步# Transaction 100提交了事务,# select 1是能查询出来,这就代表了Read View(一致性视图)是在第一次查询的时候产生的,如果是在开启事务的时候就产生的那么这里查询出来的name应该还是Kerwin。

来分析一下这一步操作的Undo Log(日志版本链)和 Read View(一致性视图)长什么样:

在这里插入图片描述

我们一共有3个事务id,20、30、100,执行到第7步,事务id为100的事务已经提交了并且是最大事务id,所以我们这里的ReadView为 [20,30], 100,活跃事务id有20、30,最大事务id为100,当我们查询的时候会拿着UndoLog中的数据和这个ReadView比对,UndoLog中第一条数据事务id为100在未提交与已提交事务区间,在判断是否存在活跃事务id中,这里活跃事务id有20、30,那么事务id为100的事务就已经提交了,取出第一条数据即可。

5.2.2、第10步 #select 1中查询id为1的数据底层处理

       在这一步中查询出来还是name=‘Kerwin100’,但是# Transaction 20事务已经提交, #select 1中查询name还是Kerwin100,这就说明了读取的是副本数据不是最新的数据。

来分析一下这一步操作的Undo Log(日志版本链)和 Read View(一致性视图)长什么样:
在这里插入图片描述

因为# Transaction 20中执行了两次更新name操作,这里UndoLog也会增加两条数据,因为我们的事务隔离级别是可重复读所以#select 1在第一次查询时就会生成一个ReadView,后面再查询都是使用的第一次查询生成的ReadView,所以这里ReadView还是为 [20,30], 100,第11步 #select 1中查询id为1的数据,先会取出UndoLog中第一条数据事务id为20和ReadView比对,因为事务id为20在活跃事务数组中,所以不满足会继续取出下一条数据进行一样的判断,直到取到事务id为100的数据,事务id为100不在活跃事务id数组中,那么事务id为100的事务就已经提交了。

5.2.3、第12步 # Transaction 30中事务内查询id为1的数据底层处理

       在#Transaction 30中先更新了id为1的数据,接着就能查询出刚刚更新的数据,这是因为ReadView中还会存储一个creator_trx_id(创建这个 Read View 的事务 ID,可以理解成当前事务ID),逐条拿UndoLog中的数据和这个ReadView比对,比对是会先判断UndoLog数据的事务id是否为creator_trx_id,如果为creator_trx_id则取出数据,如果不为creator_trx_id则进行后续判断。

来分析一下这一步操作的Undo Log(日志版本链)和 Read View(一致性视图)长什么样:
在这里插入图片描述

因为在#Transaction 30中先更新了id为1的数据还没有提交,这里UndoLog也会增加一条,但是因为#Transaction 30还未提交所以更新后在当前事务查询ReadView为 [30], 100,creator_trx_id=30,取出UndoLog第一条数据和ReadView比对,事务id等于creator_trx_id所以第一条数据满足条件直接取出。

5.2.4、第13步 # select 2中查询id为1的数据底层处理

       在这一步中查询出来还是name=‘Kerwin21’,因为这一步是 # select 2第一次查询,并且# Transaction 20事务已经提交,所以这里可以获取到# Transaction 20提交后的数据。

来分析一下这一步操作的Undo Log(日志版本链)和 Read View(一致性视图)长什么样:

在这里插入图片描述

因为# Transaction 20已经提交事务,# select 2第一次查询时生成的ReadView的活跃事务id数组就只有30了,最大事务id还是100,所以这里ReadView为 [30], 100,先会取出UndoLog中第一条数据事务id为30和ReadView比对,事务id为30的事务还在活跃事务数组中,所以不满足会继续取出下一条数据进行一样的判断,第二条事务id为20的事务小于最小活跃事务id事务已提交,取出第二条数据即可。

5.2.5、第15步 # select 2中查询id为1的数据底层处理

       在这一步中查询出来还是name=‘Kerwin21’,因为 # select 2第一次查询已经生成了ReadView,就算# Transaction 30事务已经提交,ReadView也是不会改变的。

来分析一下这一步操作的Undo Log(日志版本链)和 Read View(一致性视图)长什么样:
在这里插入图片描述
这一步和第13步逻辑是一样的。

5.3、已提交读的ReadView是如何生成的

       在可重复读中,一个事务开启后第一次查询就会生成一个ReadView,在之后的查询中都不会在改变,而已提交读会在每次查询的时候都生成一个新的ReadView,保证了每次都能读取到已经提交的数据。

例如:

# Transaction 100# select 1
1begin;begin;
2select name from account where id = 1; --readview:[100], 100 name=‘Kerwin’
3update account set name=‘Kerwin100’ where id = 1;
4commit;
5select name from account where id = 1; --readview:[], 100 name=‘Kerwin100’

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

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

相关文章

直流负载箱的维护和保养方法有哪些?

直流负载箱的维护和保养方法主要包括以下几个方面: 日常要定期清洁负载箱的外壳和散热器,确保散热良好,避免灰尘积累影响散热效果。还要定期检查负载箱的连接器,确保连接良好,避免接触不良或松动导致故障。根据使用要求…

信创之国产浪潮电脑+统信UOS操作系统体验3:使用 visual studio code搭建Python开发环境

☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython 一、引言 老猿原来在windows下开发python程序,要么使用python自带的IDLE,要么使用pycharm,IDLE用来开发很不方便,而pycharm对开发支持比较好,换成…

我做了一个简易P图(参数图)分析软件

P图(即参数图,Parameter Diagram),是一个结构化的工具,帮助大家对产品更好地进行分析。 典型P图格式 P图最好是和FMEA软件联动起来,如国可工软的FMEA软件有P图分析这个功能。 单纯的P图分析软件很少,为了方便做P图分…

vue-1

一、为什么要学习Vue 1.前端必备技能 2.岗位多,绝大互联网公司都在使用Vue 3.提高开发效率 4.高薪必备技能(Vue2Vue3) 二、什么是Vue 概念:Vue (读音 /vjuː/,类似于 view) 是一套 构建用户界面 的 渐进式 框架 …

检验样品数量

声明 本文是学习GB-T 586-2015 船用法兰铸钢止回阀. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 4 要求 4.1 材料 止回阀的主要零件材料见表4。 表4 止回阀的主要零件材料 零 件 名 称 材 料 名 称 牌 号 标 准 编 号 阀体 阀体 铸钢 ZG275-485…

使用docker-compose部署Redis(单机部署)

目录 一、查看Redis镜像版本二、拉取自己需要的镜像版本三、创建挂载目录四、添加配置文件五、编写 docker-compose.yml 文件六、启动容器七、连接测试 一、查看Redis镜像版本 先去Docker Hub查看Redis镜像有那些版本,我部署的时候Redis最新已经到7.x的版本了&…

23.4 Bootstrap 框架5

1. 背景颜色 1.1 背景颜色样式 在Bootstrap 5中, 可以使用以下类来设置背景颜色: * 1. .bg-primary: 设置为主要的背景颜色(#007bff, 深蓝色). * 2. .bg-secondary: 设置为次要的背景颜色(#6c757d, 灰色). * 3. .bg-success: 设置为成功的背景颜色(#28a745, 绿色). * 4. …

[yolo系列:yolov7添加可变形卷积Deformable Conv V2]

yolo系列文章目录 文章目录 yolo系列文章目录一、可变形卷积是什么?二、使用步骤1.在models/common.py文件添加2.然后再yolo.py里面添加DCNv23.修改yolov7的yaml 总结参考文章 一、可变形卷积是什么? 可变形卷积即DCN(缩写取自Deformable Con…

uniapp:swiper-demo效果

单元格轮播 <swiper class"swiper1" :circular"true" :autoplay"true" interval"3000" previous-margin"195rpx" next-margin"195rpx"><swiper-item v-for"(item,index) in 5" :key"inde…

平凡工作也能创造卓越:学习公文写作的逻辑与技巧

平凡工作也能创造卓越&#xff1a;学习公文写作的逻辑与技巧 前言如何把平凡的工作写出光环1.个人不能超越集体2.工作成果的概括要准确3.描写平凡工作的难点痛点 书籍介绍关键点关键词 书籍亮点内容简介购买链接参与方式往期赠书回顾 前言 如何把平凡的工作写出光环 &#x1…

C语言字符串查找函数和错误信息报告函数(strstr、strtok,strerror)

文章目录 摘要1 strstr1.1 函数使用1.2 模拟实现 2. strtok2.1 函数介绍 3. strerror3.1 函数介绍3.2 strerror 与 perror 摘要 本篇文章介绍了C语言中常用的字符串处理函数&#xff0c;包括字符串查找函数 strstr 和字符串分割函数 strtok&#xff0c;以及错误信息报告函数 s…

2023旅游产业内容营销洞察报告:如何升级经营模式,适配社媒新链路

2023年我国旅游业强劲复苏&#xff0c;上半年旅游消费增长显著&#xff0c;政府出台一系列文旅扶持政策后&#xff0c;旅游业也在积极寻求数字化转型的升级方式。 上半年以旅游消费为代表的服务业对经济的增长贡献率超过60%&#xff0c;旅游企业普遍实现经营好转&#xff0c;企…

Windows 10下安装运行ROS

Windows 10下安装运行ROS 官方教程 ROS on Windows installation 1 系统要求 Windows ROS需要64位的Windows 10 Desktop或Windows 10 IoT Enterprise。 请确保您已安装Powershell并在系统路径中 从实时病毒扫描程序中排除c:\opt(以及稍后的工作空间文件夹)&#xff0c;因为…

KALI 各种工具的使用与介绍

KALI 各种工具的使用与介绍 一、工具介绍 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关…

Linux CentOS7 yum仓库

在windows下安装一个软件很轻松&#xff0c;只要双击setup或者.exe的文件&#xff0c;安装提示连续“下一步”即可&#xff0c;然而linux系统下安装一个软件似乎并不那么轻松&#xff0c;因为我们不是在图形界面下。 本文我们将讨论如何在linux下安装一个软件。 一、linux软件…

【JavaEE初阶】 Thread类及常见方法

文章目录 &#x1f334;Thread类的概念&#x1f333;Thread 的常见构造方法&#x1f384;Thread 的几个常见属性&#x1f340;start()-启动一个线程&#x1f332;中断一个线程&#x1f6a9;实例一&#x1f6a9;实例二&#x1f6a9;实例三 &#x1f38d;join()-等待一个线程&…

如何选择UMLChina服务

服务口号&#xff1a;聚焦最后一公里 斐力庇第斯从马拉松跑回雅典报信&#xff0c;虽然已是满身血迹、精疲力尽&#xff0c;但他知道&#xff1a;没有出现在雅典人民面前&#xff0c;前面的路程都是白费。 学到的知识如果不能最终【用】于您自己的项目之中&#xff0c;也同样是…

设备搭建(waf、蜜罐、ids和ips)

文章目录 防火墙waf网闸蜜罐idsips 防火墙 DMZ区域叫非军事化区减&#xff0c;DMZ有web服务或者MySQL服务&#xff0c;从互联网到dmz的流量一般不拦截&#xff08;因为需要互联网用户访问web服务&#xff09;&#xff0c;如果dmz沦陷&#xff0c;攻击者想要继续横向移动到内网…

系统03:15min导图复习 文件管理

&#x1f433;前言 图源&#xff1a;文心一格 考研笔记整理&#xff0c;纯复习向&#xff0c;思维导图基本就是全部内容了&#xff0c;不会涉及较深的知识点~~&#x1f95d;&#x1f95d; 第1版&#xff1a;查资料、画思维导图~&#x1f9e9;&#x1f9e9; 编辑&#xff1a; …