【MySQL】MVCC与Read View

article/2025/8/3 20:03:48

目录

一、数据库并发的三种场景

二、读写场景的MVCC

(一)表中的三个隐藏字段

(二)undo 日志

(三)模拟MVCC

(四)Read View

(五)当前读和快照读

三、RC和RR隔离级别的区别


一、数据库并发的三种场景

        数据库作为存储大量数据的介质,一定存在着大量的IO操作,也就是写操作和读操作。

读-读 :不存在任何问题,也不需要并发控制;

读-写 :有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读;

写-写 :有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失。

        本文主要讨论在读写场景下的并发问题。

二、读写场景的MVCC

        MVCC即多版本并发控制协议,其是 InnoDB 存储引擎为了实现高并发事务处理的核心机制。它通过维护数据的多个版本,使读写操作可以非阻塞并行执行,从而极大提升了数据库的并发性能。

(一)表中的三个隐藏字段

        当新建一个表结构时,除了显示定义的列结构,表中还包含了三个隐藏字段:

  • DB_TRX_ID:6字节,记录最近修改本条记录的事务ID;
  • DB_ROLL_PTR:7字节,回滚指针,记录该条记录的上一版本;
  • DB_ROW_ID:6字节,当数据表没有主键时,InnoDB会自动以 DB_ROW_ID 作为隐藏主键并建立一个聚簇索引。

        例如向一张表插入一条数据时,实际该数据内容为:

nameageDB_TRX_IDDB_ROLL_PTRDB_ROW_ID
张三18最近修改本条记录的事务IDnull(新增数据因此没有上个版本)1(隐藏主键)

        实际数据表还有个删除 flag 隐藏字段,用于表明该条是否有效,也就是删除表中数据时是逻辑删除,之后在合适的时候由 MySQL 再向磁盘刷新数据。

(二)undo 日志

        undo 日志是在 MySQL 中的一段内存缓冲区,用于保存日志文件。其主要由两个核心作用:

  • 事务回滚:
    当事务执行失败或者主动回滚时,undo log 中记录的数据旧版本课用于恢复原始状态。例如:当执行插入数据操作时,undo log 会记录其对应的删除操作,回滚时直接执行该操作删除数据;当执行更新或删除操作时,undo log会记录数据的旧值用于回滚。
    事务在修改数据前,undo log会记录反向操作或数据旧值,形成逻辑日志链。
  • 支持MVCC:
    undo log会存储数据的历史版本,通过隐藏字段 DB_TRX_ID 和 DB_ROLL_PTR串联历史版本,形成链式日志从而支持 MVCC 。详见下文。

(三)模拟MVCC

        假定 student 表中已有数据如下:

        假定有个事务10,对 student 表中记录进行了修改,将姓名修改为了"李四"。当事务10执行完毕后:

        在此过程中,事务10首先会将该条记录加行锁,修改前先将该条记录拷贝到 undo log 中(写时拷贝)。之后将数据修改为目标值并再填写相应的字段,之后事务提交后并释放锁。

        假定现在有个事务11对表中数据进行修改,将年龄改为了20,事务11也会进行以上的操作。当事务11执行完毕后:

        undo log中的一个个版本被称为快照。正如上文所述,除了记录版本链以外,undo log 还会记录相反的操作以备回滚。

        当执行插入操作时,undo log 会基于主键记录对应相反的删除操作;当执行删除操作时会将该记录的删除 flag 字段设置为删除即逻辑删除,并将该条数据记录在 undo log 中;当执行select 操作时,会根据隔离级别执行当前读或者快照读。当前读即读取最新的数据,快照读即读取数据的历史版本。

        针对于 select 操作,在RU隔离级别下所有查询都是读取最新版本的数据,RC和RR隔离级别下所有普通查询都是快照读,而Serializable隔离级别下事务是严格串行执行,因此所有查询操作都是当前读。本文主要讨论如何MVCC 机制如何解决 RC 和 RR隔离级别下的读写并发问题。

        隔离级别和读写并发问题详见:【MySQL】事务及隔离性-CSDN博客

(四)Read View

        Read View 是事务首次进行快照读时由 MySQL 生成的,其主要是配合 MVCC 机制进行版本控制。

        当某个事务执行 select 快照读的时候,MySQL新建一个 Read View 对象,用其内部的字段来判断当前事务应该读取数据的哪个版本,该数据可能是当前最新的数据,也有可能是该行记录的 undo log 里面的某个版本的数据,这由隔离级别决定。

        以下是简化 Read View 的结构体:

class ReadView {// 省略...private:/** 高水位,大于等于这个ID的事务均不可见*/trx_id_t m_low_limit_id/** 低水位:小于这个ID的事务均可见 */trx_id_t m_up_limit_id;/** 创建该 Read View 的事务ID*/trx_id_t m_creator_trx_id;/** 创建视图时的活跃事务id列表*/ids_t m_ids;//ids_t集合类型 /** 配合purge,标识该视图不需要小于m_low_limit_no的UNDO LOG,* 如果其他视图也不需要,则可以删除小于m_low_limit_no的UNDO LOG*/trx_id_t m_low_limit_no;/** 标记视图是否被关闭*/bool m_closed;// 省略...
};

m_ids:一张列表,用来维护Read View生成时刻,系统正活跃的事务ID
up_limit_id:记录m_ids列表中事务ID最小的ID
low_limit_id:ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1
creator_trx_id:创建该ReadView的事务ID

        表中的三个隐藏字段配合 Read View 即可完成 MVCC机制。

        当一个事务进行快照读时,MySQL 会为此建立一个 Read View 对象,首先 up_limit_id 字段会记录当前活跃事务的最小事务ID,low_limit_id 会记录当前活跃时最大事务ID值 + 1, 而 m_ids 会记录当前所有活跃的事务ID。例如:当事务10创建 Read View 对象时,假定活跃事务有(8,9,12),那么事务是10对应的字段值分别为:up_limit_id = 8, low_limit_id = 13, m_ids = (8, 9, 12)。

        下面将展开说明 Read View 是如何配合 undo log 实现 MVCC机制的:

        当一个新事务执行普通 select 操作(快照读)时,MySQL 会为此新建并初始化一个 Read View 对象,针对目标数据存在以下的情况:

        若该条记录的 DB_TRX_ID 小于 Read View 中的 up_limit_id 最小事务ID,说明修改该记录的事务在新事务到来之前就已经执行完毕提交了,故该数据可被新事物所见,无需查看该条记录的上一版本了;

        若该条记录的 DB_TRX_ID 大于等于 Read View 中的 low_limit_id 最大事务ID,说明修改该记录的事务在新事务执行查询操作之后才执行完毕(不一定提交),若该条记录存在上一版本,则需通过该条记录的 DB_ROLL_PTR 字段查询上一版本并再次进行比较;若不存在上一版本,则该条记录不可被新事务所见;

        若该条记录的 DB_TRX_ID 处于 up_limit_id 与 low_limit_id 之间,则需要进一步判断。若该条记录的 DB_TRX_ID 存在于 m_ids 列表中,则说明新事务执行查询操作时 DB_TRX_ID 该事务仍处于活跃状态,因此该条记录不可见,需查询该记录上一版本进一步进行判断;若该条记录的 DB_TRX_ID 不存在于 m_ids 列表中,则说明新事务执行查询操作时 DB_TRX_ID 该事务已经执行完毕提交了,则该条记录可以被新事务所见,无需查看该条记录的上一版本了。

(五)当前读和快照读

        在上文中我们铺垫了当前读和快照读的概念,那么应该如何操作呢?以下示例都是在 RR 隔离级别下进行测试。

        两个事务同时开启,由上面两张图可知,MySQL会为快照读建立 Read View 对象,因此不同的读取可能会造成不同的查询结果。(图一是修改年龄为20,但查询结果为18/图二是修改年龄为18,查询结果也为18)

        当前读与快照读:

//当前读
mysql> select * from student lock in share mode;
//快照读
mysql> select * from student;

三、RC和RR隔离级别的区别

        RC和RR隔离级别下在快照读时都会生成 Read View 对象,正是生成 Read View 对象的时机不同,导致快照读的结果不同

在RC隔离级别下:

        每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因,也就是每个快照读都会生成最新的 Read View 对象。正是RC每次快照读,都会形成Read View,所以,RC才会有不可重复读问题

在RR隔离级别下:

        同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。也就是只有第一次进行快照读时才会生成 Read View 对象,此后在调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可 见;


http://www.hkcw.cn/article/dyBobDwaOt.shtml

相关文章

代码随想录打卡|Day53 图论(Floyd 算法精讲 、A * 算法精讲 (A star算法)、最短路算法总结篇、图论总结 )

图论part11 Floyd 算法精讲 代码随想录链接 题目链接 代码 三维DP数组 import java.util.Scanner;public class Main {// 定义最大距离值,避免使用Integer.MAX_VALUE防止加法溢出public static final int INF 100000000; // 10^8足够大且不会溢出public static…

CSS Day07

1.搭建项目目录 2.网页头部SEO三大标签 3.Favicon图标与版心 (1)Favicon图标 (2)版心 4.快捷导航 5.头部-布局 6.头部-logo 7.头部-导航 8.头部-搜索 9头部-购物车 10.底部-布局 11.底部-服务区域 12.底部-帮助中心 13.底部-版权…

leetcode hot100刷题日记——29.合并两个有序链表

解答: 方法一:递归 递归的边界条件是啥呢? 递归别想那么多具体步骤,考虑大步骤,小的递归自己会去做的 class Solution { public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {//递归比较大小//先考虑…

Spring Boot 整合 Spring Security

DAY30.1 Java核心基础 Spring Boot 整合安全框架 Spring Security 、Shiro Spring Security Spring Security 的核心功能包括认证、授权、攻击防护,通过大量的过滤器和拦截器进行请求的拦截和验证,实现安全校验的功能。 Spring Security 将校验逻辑…

深度剖析Node.js的原理及事件方式

早些年就接触过Node.js,当时对于这个连接前后端框架就感到很特别。尤其是以独特的异步阻塞特性,重塑了了服务器端编程的范式。后来陆陆续续做了不少项目,通过实践对它或多或少增强了不少理解。今天,我试着将从将从原理层剖析其运行…

智慧景区一体化建设方案

随着2023年文旅部《关于推动智慧旅游发展的指导意见》出台,全国景区掀起数字化转型浪潮。如何在激烈竞争中脱颖而出?智慧景区一体化建设方案,正以“一机游遍景区、一屏掌控全局”的革新模式,重新定义旅游体验与管理效率。本文深度…

使用 SymPy 操作三维向量的反对称矩阵

在三维空间中,一个 3 1 3 \times 1 31 向量可以转换为一个 3 3 3 \times 3 33 的反对称矩阵。这种转换在物理学、机器人学和计算机视觉等领域非常有用。本文将详细介绍如何在 Python 的 SymPy 库中定义和使用这种反对称矩阵。 数学背景 对于一个三维向量 v …

LangChain表达式(LCEL)实操案例1

案例1:写一篇短文,然后对这篇短文进行打分 from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.runnables import RunnableWithMessageHist…

CppCon 2014 学习:HOW UBISOFT MONTREAL DEVELOPS GAMES FOR MULTICORE

多核处理器(Multicore Processor) 的基本特性,下面是对每点的简要说明: 🔹 Multicore(多核) 指一个物理处理器上集成了 多个 CPU 核心,每个核心可以独立执行指令。 🔸…

STL解析——String类详解(使用篇)

目录 sring接口解析 1.string简介 2.默认成员函数 2.1构造函数 2.2析构函数 2.3赋值重载 3.迭代器 3.1初识迭代器 3.2迭代器的使用 3.3特殊迭代器 3.4范围for 4.大小接口 4.1字符长度相关接口 4.2空间大小相关接口 5.其他常用接口 5.1operator[ ] 5.2增 5.3查 5…

Android 代码阅读环境搭建:VSCODE + SSH + CLANGD(详细版)

在阅读Android源码(AOSP超过1亿行代码)时,开发者常面临索引失败、跳转卡顿等问题。本教程将手把手教你搭建基于VSCode SSH Clangd的终极阅读环境,实现秒级符号跳转、精准代码提示和高效远程开发。 一、环境架构解析 1.1 方案组…

JAVA 集合的进阶 泛型的继承和通配符

1 泛型通配符 可以对传递的类型进行限定 1.1 格式 ? 表示不确定的类型 ?extends E: 表示可以传递 E 或者 E 所有的子类类型 ?super E: 表示可以传递 E 或者 E 所有的父类类…

改写自己的浏览器插件工具 myChromeTools

1. 起因, 目的: 前面我写过, 自己的一个浏览器插件小工具 最近又增加一个小功能,可以自动滚动页面,尤其是对于那些瀑布流加载的网页。最新的代码都在这里 2. 先看效果 3. 过程: 代码 1, 模拟鼠标自然滚动 // 处理滚动控制逻辑…

由sigmod权重曲线存在锯齿的探索

深度学习的知识点,一般按照执行流程,有 网络层类型,归一化,激活函数,学习率,损失函数,优化器。如果是研究生上课学的应该系统一点,自学的话知识点一开始有点乱。 一、激活函数Sigmod…

仿腾讯会议——优化:多条tcp连接

1、添加用户信息结构 2、添加注册视频音频结构体 3、 完成函数注册视频音频

File—IO流

因为变量,数组,对象,集合这些数据容器都在内存中,一旦程序结束,或者断电,数据就丢失了。想要长久保存,就要存在文件中(File) 文件可以长久保存数据。 文件在电脑磁盘中…

【Zephyr 系列 2】用 Zephyr 玩转 Arduino UNO / MEGA,实现串口通信与 CLI 命令交互

🎯 本篇目标 在 Ubuntu 下将 Zephyr 运行在 Arduino UNO / MEGA 上 打通串口通信,实现通过串口发送命令与反馈 使用 Zephyr Shell 模块,实现 CLI 命令处理 🪧 为什么 Arduino + Zephyr? 虽然 Arduino 开发板通常用于简单的 C/C++ 开发,但 Zephyr 的支持范围远超 STM32…

最悉心的指导教程——阿里云创建ECS实例教程+Vue+Django前后端的服务器部署(通过宝塔面板)

各位看官老爷们,点击关注不迷路哟。你的点赞、收藏,一键三连,是我持续更新的动力哟!!! 阿里云创建ECS实例教程 注意: 阿里云有300元额度的免费适用期哟 白嫖~~~~ 注册了阿里云账户后&#x…

【Android】如何抓取 Android 设备的 UDP/TCP 数据包?

目录 前言理解抓包tcpdump 实时抓包Wireshark 解包抓包后的一些思考 前言 在真正接触 UDP/TCP 抓包之前,我一直以为这是一项高深莫测的技术。可当我们真正了解之后才发现,其实并没有那么复杂——不过如此。 所谓的大佬,往往只是掌握了你尚未…

VR看房系统,新生代看房新体验

VR看房系统的概念 虚拟现实(VirtualReality,VR)看房系统,是近年来随着科技进步在房地产行业中兴起的一种创新看房方式。看房系统利用先进的计算机技术模拟出一个三维环境,使用户能够身临其境地浏览和体验房源,无需亲自…