【MVCC】深入浅出彻底理解MVCC

news2024/11/27 22:31:18

MVCC概述

MVCC(Multi-Version Concurrency Control)即多版本并发控制。主要是为了提高数据库的并发性能而提供的,采用了不加锁的方式处理读-写并发冲突,确保了任何时刻的读操作都是非阻塞的。只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能。所以我们也可以说MVCC是一种用来解决读-写冲突的无锁并发控制

引入

假设我发表了一篇博客,检查的时候发现了有几个错字,打算去修改,修改完之后要重写发布并审核。那此时对其他正在看文章的读者来说,他们是不是看不到了呢,答案是否定的,他们依然能够看到,不过看到的还是有错别字的版本,即旧版本。当审核通过之后就用新版本去覆盖了旧版#本,此时看到的文章就是已经修改过的新版本。

MVCC多版本并发控制

而对于InnoDB的MVCC机制来说,思路也大致相同。并发读写是存在线程安全问题的,有可能出现脏读、幻读、不可重复读。而MVCC的读其实是快照读,快照读读取到的数据不一定是最新的,又可能是历史的版本。也即让并发的事务的读写操作作用于不同的版本,比如读老版本,写新版本,这样无论执行写操作的事务干了啥都不会影响读的事务。
需要注意的是MVCC只在RCRR两个隔离级别下工作。如果是RU的话,允许脏读的存在,即一个事务可以读取到另一个事务未提交的数据,自然可以读到最新的版本,与MVCC冲突。如果是SERIALIZABLE,所有的事务都是串行的,不存在并发,因此也就没有MVCC什么事了。虽然MVCC在RCRR下工作,但是他们的实现方式又是不同的。

实现原理

MVCC主要是通过隐藏字段Undo-log日志以及ReadView来实现的。

隐藏字段

数据库表中的每一行除了我们显示定义的几个字段之外,还构建了一些InnoDB引擎的隐藏字段,主要的有DB_ROW_IDDB_Deleted_BitDB_TRX_IDDB_ROLL_PTR

  • DB_ROW_ID:隐藏主键,6Bytes。InnoDB存储引擎是按照主键作为聚簇索引列来构建B+树存储的,如果表中没有主键,那么就选择一个唯一非空的字段。如果两种都没有就InnoDB会自动以DB_ROW_ID产生一个聚簇索引,只不过这个索引在上层无法使用,仅提供给InnoDB构建树结构存储表数据。
  • DB_Deleted_Bit:删除标识,1Bytes。这里不做过多赘述。
  • DB_TRX_ID:最近修改/插入事务ID,6Bytes。MySQL对于每一个创建的事务,都会为其分配一个事务ID,事务ID同样遵循顺序递增的特性,即后来的事务ID绝对会比之前的ID要大。但是对于select查询语句,其事务id=0。如果是手动开启的事务,无论是否是select,MySQL都会为其分配事务ID。而TRX_ID记录的就是最近一次改动当前这条数据的事务ID。
  • DB_ROLL_PTR:回滚指针,7Bytes。指向这条记录的上一个版本(存储于 rollback segment 里)。当一个事务对一条数据做了改动后,都会将旧版本的数据放到Undo-log日志中,而DB_ROLL_PTR就是一个地址指针,指向Undo-log日志中旧版本的数据,当需要回滚事务时,就可以通过这个隐藏列,来找到改动之前的旧版本数据,而MVCC机制也利用这点,实现了行数据的多版本。

Undo-log

在InnoDB中undo日志一共有两种类型,分别是Update Undo LogInsert Undo Log

  • Update Undo Log:这种类型的Undo日志主要用来存储更新数据之前的原始信息,其主要目标是满足在事务执行过程中的数据修改引发的回滚操作;并用于在读取数据时,保证数据的一致性。
  • Insert Undo Log:在插入操作中产生的日志。只在事务回滚时需要,InnoDB并不需要保存完整的行数据信息,它主要用于标记这条新插入的记录在事务完成之前对其他事务是不可见的。
    假设有一个person表,表中存储的数据如下:
    ![[Pasted image 20240411134955.png]]

执行这样两个事务:

start transaction;
update person set user_name = "李四" where u_id = 1;
commit;
start transaction;
update person set sex = "女" where u_id = 2;
commit;

其实就会出现这样一条版本链,旧版本的信息存储在undo日志中,新的输入的db_roll_ptr指向的只上一个版本的地址。
![[Pasted image 20240411140059.png]]

值得注意的是,新版本数据都会插入到链表头中,而不是追加到链表尾部。

update的执行过程

  1. 对ID=1这条要修改的行数据加上排他锁
  2. 将原本的旧数据拷贝到Undo-log的rollback Segment区域。
  3. 对表数据上的记录进行修改,修改完成后将隐藏字段中的trx_id改为当前事务ID。
  4. 将隐藏字段中的roll_ptr指向Undo-log中对应的旧数据,并在提交事务后释放锁。

Undo-log日志要设计出版本链,一方面可以实现事务回滚,另一方面则可以实现MVCC机制

ReadView

如果t2事务要查询一条行数据,此时这条数据正在被他t1事务修写,那么这条行数据也就可能存在多个旧版本数据,t2在查询的时候,应该查询哪个旧版本的数据呢?此时就需要ReadView。具体来说,当一个事务需要读取数据时,InnoDB会创建一个ReadView实例,为该事务提供一个数据的“快照”,在这个快照中记录着当前所有活跃事务的ID(活跃事务是指还在执行的事务,即未结束(提交/回滚)的事务)。

当一个事务启动后,首次执行select操作时,MVCC就会生成一个数据库当前的ReadView,通常而言,一个事务与一个ReadView属于一对一的关系(不同隔离级别下也会存在细微差异),ReadView一般包含四个核心内容:

  • creator_trx_id:当前创建这个ReadView的事务id
  • trx_ids:在生成当前的ReadView之前,系统内活跃的事务id列表
  • up_limit_id:活跃的当前事务列表中,最小的事务id
  • low_limit_id:生成当前ReadView时,系统要给下一个任务分配的事务id。

看一个ReadView示意图:
![[Pasted image 20240411165934.png]]

假设当前数据库中有t1~t5这5个事务,其中活跃着的事务时t1,t2,t4,t3几经回滚,t5已经提交,此时有一条select语句执行时,就会生成一个ReadView,没有开启事务进行select,而是直接select,那么就会为其分配trx_id=0。所以产生快照的信息是:

{
    "creator_trx_id" : "0",
    "trx_ids" : "[1,2,4]",
    "up_limit_id" : "1",
    "low_limit_id" : "6"
}

MVCC机制的实现原理

当一个事务尝试修改某条数据时,会将表中的旧数据放入udno日志中;当一个事务查询某条数据的时候,MVCC会生成一个ReadView快照读。其中Undo-log主要实现数据的多版本,ReadView则主要实现多版本的并发控制。

执行过程

  1. 当事务中出现select语句时,会生成一个ReadView
  2. 判断数据行中的隐藏列trx_idReadView.creator_trx_id是否相等。
    • 相等:表示创建ReadView快照和修改行数据(修改行数据这个事务生成的就是trx_id)是同一个事务,那么这个事务自然可以读取到这行最新版本的数据。
    • 不相等:代表目前要查询的数据,是被其他事务修改过的,继续下面的判断
  3. 判断数据行中的隐藏列trx_idReadView.up_limit_id的大小关系。
    • 前者小于后者:表示修改行数据的这个事务,在创建快照前就已经完成,可以读取最新版本的数据。
    • 前者大于等于后者:代表改动行数据的事务还在执行,继续进行判断
  4. 判断数据行中的隐藏列trx_idReadView.low_limit_id这个值的大小关系。
    • 前者大于等于后者:那么表示这行数据是在创建ReadView(即,当前事务开始)之后被修改的,因此不能访问最新版数据。
    • 前者小于后者:表示改动行数据的事务ID在up_limit_id、low_limit_id之间,需要进一步判断。
  5. 继续判断trx_id是否在trx_ids中。
    • :表示改动行数据的事务目前依旧在执行,不能访问最新版数据。
    • 不在:表示改动行数据的事务已经结束,可以访问最新版的数据。

这一块我的整理还是比较抽象的,建议再去看看文件末尾的参考资料,再去理解和消化。

RC、RR下的MVCC

  • 在RC隔离级别下,是每个快照读都会生成并获取最新的ReadView;
  • 在RR隔离级别下,则是同一个事务中的第一个快照读才会创建ReadView, 之后的快照读获取的都是同一个ReadView。

参考资料

  • MySQL之MVCC机制:为什么你改了的数据我还看不见?
  • MVCC详解,深入浅出简单易懂
  • 【MySQL笔记】正确的理解MySQL的MVCC及实现原理

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

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

相关文章

机器学习和深度学习 -- 李宏毅(笔记与个人理解)Day 13

Day13 Error surface is rugged…… Tips for training :Adaptive Learning Rate critical point is not the difficult Root mean Square --used in Adagrad 这里为啥是前面的g的和而不是直接只除以当前呢? 这种方法的目的是防止学习率在训练过程中快速衰减。如果只用当前的…

文心一言

文章目录 前言一、首页二、使用总结 前言 今天给大家带来百度的文心一言,它基于百度的文心大模型,是一种全新的生成式人工智能工具。 一、首页 首先要登录才能使用,左侧可以看到以前的聊天历史 3.5的目前免费用,但是4.0的就需要vip了 二、使用 首先在最下方文本框输入你想要搜…

PostgreSQL15 + PostGis + QGIS安装教程

目录 下载1、PostgreSQL安装1.1、环境变量配置 2、PostGIS安装2.1、安装插件 3、QGIS下载3.1、安装3.2、测试 下载 PostgreSQL15安装:下载地址 PostGIS安装:下载地址(倒数第二个) 1、PostgreSQL安装 下载安装包之后一直点下一步…

Redis从入门到精通(十五)Redis分布式缓存(三)Redis分片集群的搭建和原理分析

文章目录 前言5.4 分片集群5.4.1 搭建分片集群5.4.2 散列插槽5.4.3 集群伸缩5.4.3.1 需求分析5.4.3.2 创建新的Redis实例5.4.3.3 添加新节点到Redis集群5.4.3.4 转移插槽 5.4.4 故障转移5.4.4.1 自动故障转移5.4.4.2 手动故障转移 5.4.5 RedisTemplate 5.5 小结 前言 Redis分布…

【单片机毕业设计8-基于stm32c8t6的RFID校园门禁系统】

【单片机毕业设计8-基于stm32c8t6的RFID校园门禁系统】 前言一、功能介绍二、硬件部分三、软件部分总结 前言 🔥这里是小殷学长,单片机毕业设计篇8基于stm32的RFID校园门禁系统 🧿创作不易,拒绝白嫖可私 一、功能介绍 -----------…

[Python图像识别] 五十二.水书图像识别 (2)基于机器学习的濒危水书古文字识别研究

该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门、OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子、图像增强技术、图像分割等,后期结合深度学习研究图像识别、图像分类应用。目前我进入第二阶段Python图像识别,该部分主要以目标检测、图像…

亚远景科技-ASPICE 4.0-HWE硬件过程的范围 The Technical Scope of HW process

ASPICE 4.0中的HWE process是电气和电子硬件的技术范畴,涵盖了硬件工程中的需求分析、设计和验证活动,但不包括以下活动: 1. 系统级工程过程。既不包括机电一体MECHATRONIC,也不包括ECU特定电子控制单元的开发。 2. 硬件采购过程…

Redis 与 MySQL 数据一致性问题

1. 什么是数据库与缓存一致性 数据一致性指的是: 缓存中存有数据,缓存的数据值 数据库中的值;缓存中没有该数据,数据库中的值 最新值。 反推缓存与数据库不一致: 缓存的数据值 ≠ 数据库中的值;缓存或…

什么是One-Class SVM

1. 简介 单类支持向量机,简称One-Class SVM(One-Class Support Vector Machine),是一种用于异常检测的监督学习算法。其主要目标是找出数据集中的异常或罕见样本,而不需要大量的正常样本用于训练。这使其在处理高维数据和非常稀疏的异常检测问…

AutoCAD之DWF三维信息提取---linux编译篇

1. 权限 1.1 给文件添加执行权限 chmod x autogen.sh1.2.给当前文件下的所有文件改变为读写执行权限 chmod 777 * -R 2.环境安装 2.1安装automake 1.4.1 安装链接 安装中遇到的问题及解决 2.2安装autoconf 2.3 安装libtool 2.4 安装Cmake(CMake包含) cmake安装在cent…

GMSSL-通信

死磕GMSSL通信-C/C++系列(一) 最近再做国密通信的项目开发,以为国密也就简单的集成一个库就可以完事了,没想到能有这么多坑。遂写下文章,避免重复踩坑。以下国密通信的坑有以下场景 1、使用GMSSL guanzhi/GmSSL进行通信 2、使用加密套件SM2-WITH-SMS4-SM3 使用心得 ​…

影响小程序SSL证书收费标准的因素有哪些?

在当今互联网时代,移动应用发展日新月异,小程序逐渐成为广大企业和个人开发者的心仪之选。然而,伴随小程序的广泛应用,安全问题和用户信任显得尤为关键。为了确保小程序的信息传输安全,SSL证书成为了一项基础配置。那么…

【C++题解】1028 - 输入一个三位数,把个位和百位对调后输出

问题:1028 - 输入一个三位数,把个位和百位对调后输出 类型:基础问题 题目描述: 输入一个三位自然数,然后把这个数的百位数与个位数对调,输出对调后的数。 输入: 输入一行,只有一…

独一无二:探索单例模式在现代编程中的奥秘与实践

设计模式在软件开发中扮演着至关重要的角色,它们是解决特定问题的经典方法。在众多设计模式中,单例模式因其独特的应用场景和简洁的实现而广受欢迎。本文将从多个角度详细介绍单例模式,帮助你理解它的定义、实现、应用以及潜在的限制。 1. 什…

C++格式化输出开源库fmt入手教程

fmt项目快速上手指南 1. cmake环境配置 include(FetchContent) FetchContent_Declare(fmtGIT_REPOSITORY https://github.com/fmtlib/fmtGIT_TAG 10.0.0GIT_SHALLOW TRUE) # 1. 下载fmt库 FetchContent_MakeAvailable(fmt)add_executable(fmt_guide main.cpp) # 2. 链接fmt库…

Java 自定義 List<T> 分頁工具

Java 自定義 List 分頁工具 PS: T可修改为对应的实体 rt com.google.common.collect.Lists;import java.util.Arrays; import java.util.Collections; import java.util.List;/*** ClassName: MyPageHelper* Descripution: List<T>分頁工具**/ public class MyPageHelp…

自动化测试(selenium篇)

这次我们来介绍selenium 我们主要来讲解这几个要点 1.什么是自动化测试 2.什么是selenium 3.为什么来讲selenium 4.selenium的环境搭建 5.selenium的 API 1.什么是自动化测试 自动化测试指软件测试的自动化&#xff0c;在预设状态下运行应用程序或者系统&#xff0c;预设条…

Android Studio 使用Flutter开发第一个Web页面(进行中)

附上Flutter官方文档 1、新建Flutter项目&#xff08;需要勾选web选项&#xff09; 新建项目构成为&#xff1a; 2、配置 Flutter 使用 path 策略 官方文档 在main.dart中&#xff0c;需要导入flutter_web_plugins/url_strategy.dart包&#xff0c;并在main(){}函数中usePath…

水经微图IOS版5.2.0发布

随时随地&#xff0c;微图一下&#xff01; 水经微图&#xff08;简称“微图”&#xff09;IOS新版已上线。 在该版本中主要新增图层树节点排序功能、常规&#xff08;矩形、圆、椭圆、扇形&#xff09;绘制功能、地形夸张等主要功能。 当前版本 当前版本号为&#xff1a;5…

html-蜘蛛

<!doctype html> <html> <head> <meta charset"utf-8"> <title>彩虹蜘蛛-jq22.com</title> <script src"http://www.jq22.com/jquery/jquery-1.10.2.js"></script> <style> </style> </he…