【MySQL】MVCC机制(undo log,read view)
文章目录
- 前言
- 一. 预备知识
- 二. 模拟MVCC
- 三. Read View
- 四. RC与RR的本质区别
- 结束语
前言
MVCC(多版本并发控制)是一种用来解决
读-写冲突的无锁并发控制
MVCC为事务分配单向增长的事务ID,为每个修改保存一个版本,版本与事物ID相关联,读操作只读该事务开始前的数据库的快照,所以MVCC可以解决以下问题
- 在并发读写数据库数据时,读操作不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写性能
- 同时解决脏读,幻读,不可重复读等事务隔离性问题,但不能解决更新丢失问题
数据库并发的场景
数据库并发场景有三种
读-读:不存在任何问题,不需要并发控制,但有使用共享锁读-写:有线程安全问题,可能会造成事务隔离性问题,如脏读,幻读,不可重复读写-写:有线程安全问题,可能会存在更新丢失问题,如第一类丢失更新问题和第二类更新丢失问题
补充
第一类更新丢失问题(回滚丢失,Lost update)
第一类更新丢失是指,一个事务被撤销,可能导致其他事务已提交的更新数据被覆盖
| 时间序号 | 事务一 | 事务二 |
|---|---|---|
| T1 | begin开启事务 | begin开启事务 |
| T2 | 查询余额money=1000 | 查询余额money=1000 |
| T3 | 存款100,money=1100 | |
| T4 | 取款100,money=900 | |
| T5 | commit提交事务 | |
| T6 | 回滚取款操作,money恢复1000 |
正如上述事例,事务一二开始查询余额都是1000,事务二先进行存款操作,并提交。
事务一不知道事务二的存在,进行取款操作,但是又进行了回滚,就会将余额恢复成最开始查询的数额,这就覆盖了事务二的更新操作
第二类更新丢失问题(覆盖丢失/两次更新问题,Second lost update)
第二类更新丢失是指,当两个事物或多个事务查询相同的记录,然后各自基于查询结果更新数据
| 时间序号 | 事务一 | 事务二 |
|---|---|---|
| T1 | begin开启事务 | begin开启事务 |
| T2 | 查询余额money=1000 | 查询余额money=1000 |
| T3 | 取款100,money=900 | |
| T4 | commit提交事务 | |
| T5 | 存款100,money=1100 | |
| T6 | commit提交事务 |
事务一二查询相同余额=1000,事务二先进行取款操作,money=900,但事务一后续基于自己的查询结果,进行存款操作,money=1100,这就覆盖了事务二的数据更新
一. 预备知识
学习MVCC前,我们要有以下三个知识了解
- 3个记录隐藏字段
undo日志(undo log)Read View
3个记录隐藏字段
这3个字段是记录信息
DB_TRX_ID:6byte。最近修改该记录的事务ID,记录创建这条记录/最后一次修改改记录的事务IDDB_ROLL_PTR:7byte。回滚指针,指向这条记录的上一个版本(指向历史版本,历史版本在undo log中)DB_ROW_ID:6byte。隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
补充:实际还有一个标记删除/更新的flag字段,在事务中删除记录,会将该flag字段标记为删除
比如如下学生表,有name和age两个属性
mysql> select * from student;
+--------+-----+
| name | age |
+--------+-----+
| 张三 | 28 |
+--------+-----+
但其实还有3个隐藏字段
| name | age | DB_TRX_ID | DB_ROW_ID | DB_ROLL_PTR |
|---|---|---|---|---|
| 张三 | 28 | 最后修改该记录的事务ID | 隐式主键 1 | 回滚指针(指向历史记录) |
undo log
MySQL是网络进程服务,所有的索引,事务,隔离性,日志等,都是在内存中完成的,即在MySQL内部的相关缓冲区中保存数据,再在合适的时候,进行刷盘,将数据写入磁盘,达到持久化
所以,undo log简单理解,就是MySQL中的一段内存缓冲区,用来保存日志数据
在数据库事务开始之前,MySQL会将记录保存在undo log中,如果事务回滚或者数据库崩溃时,可以利用undo log日志中记录的日志信息进行
回退。同时也可以提供多版本并发控制下的读(MVCC)
undo log的生命周期
undo log产生:在事务开始之前生成
undo log销毁:当事务提交之后,undo log并不能马上删除,而是放入待清理的链表,由purge线程判断是否有其他事务在使用undo log保存的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间
注意:undo log也会产生redo log,undo log也需要持久化保护
undo log和redo log的区别
undo log是逻辑日志,实现事务的
原子性
- undo log记录的是事务开始前的数据状态,记录的是更新前的值
- undo log实现事务的
原子性(提供回滚)redo log是物理日志,实现事务的
持久性
- redo log记录的是事务完成后的数据状态,记录的是更新后的值
- redo log实现事务的持久性(保证数据的完整性)
Read view稍后再讲解,因为需要快照这一概念
二. 模拟MVCC
假设现在有一个事务,其事务ID为10,对student表中记录进行修改update:将name(张三)改成name(李四)
MVCC过程如下:
- 因为是修改,所以要给该记录加行锁
- 修改前,先将原本的数据拷贝一份到undo log中,相当于undo log中有一个备份(写时拷贝)
- 然后,MySQL相当于有两行相同的记录,修改是修改原始记录的name,并且修改原始记录的隐藏字段DB_TRX_ID为修改该数据的事务ID,即10,而该记录的回滚指针DB_ROLL_PTR,里面写入undo log中副本数据的地址,表示上一个版本
- 事务10提交,释放锁
结果如下图

此时,最新的记录就是name='李四’的那条
接着,又有一个事务11要对student表进行修改(update):将age(28)改成age(38)
- 因为是修改,所以需要给该记录加行锁
- 修改前,拷贝一份原始数据到undo log中
- 将原始数据的age(28)改成age(38),并且修改DB_TRX_ID为事务11ID,DB_ROLL_PTR指向undo log中的备份数据地址,表示上一个版本数据
结果如下图

如此就形成了一个基于链表记录的历史版本链。回滚其实就是利用历史数据,覆盖当前数据
上述的一个个版本,被称为一个个快照
update可以形成版本,delete和insert同样也可以。
delete删除数据是设置
flag字段为删除,回滚只要再修改flag字段即可
insert插入数据,虽然没有历史版本,但是为了回滚操作,insert的数据也会被放入undo log中,如果当前事务commit提交了,那么undo log的历史insert记录就会被清空
有了undo log,select读取就被分为了两种读:
- 快照读,读取历史版本
- 当前读,读取最新数据,select lock in share mode(共享锁),select for update。增删改也是读取当前数据
当有多个事务同时增删改时,都是当前读,势必需要加锁,此时select如果也是当前读,那就会被阻塞,这就是串行化
但如果是快照读,读取历史版本,则不受加锁限制,可以并发运行,这就是MVCC的意义。
隔离级别决定了select是当前读还是快照读
事务总是有先有后,而事务可以分为三个阶段:执行前,执行中,执行后
隔离性的目的就是让不同的事务看到它该看到的内容
三. Read View
如何实现隔离级别呢?其实就是实现了Read View
Read View是事务进行快照读操作时产生的一个读视图,在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(每个事务开始时,都会被分配到一个ID,此ID是自增的,事务越新,ID值越大)
Read View在MySQL源码中,是一个类。本质是用来进行可见性判断的。即当我们某个事务执行快照读时,对该记录创建一个Read View读视图,以此判断当前事务能够看到哪个版本的数据,既可能是当前最新数据,也可能是该记录在undo log里的某个历史版本数据
比较关键的属性如下:
class ReadView
{
...
private:
trx_id_t m_low_limit_id;
trx_id_t m_up_limit_id;
trx_id_t m_creator_trx_id;
ids_t m_ids;
bool m_closed;
...
}
m_ids:创建视图时的活跃事务id列表m_low_limit_id:翻译为高水位,生成ReadView时,系统尚未分配的下一个事务ID,也就是目前已有的事务ID的最大值+1,大于等于这个ID的事务均不可见m_up_limit_id:翻译为低水位,记录m_ids列表中事务ID的最小ID,小于这个ID的事务均可见m_creator_trx_id:创建该读视图的事务ID
我们在实际读取数据版本链的时候,能读取到每一个版本对应的事务ID,也就是隐藏字段DB_TRX_ID
而通过DB_TRX_ID和以上四个属性作比较,就可以判断该记录是否应该被读取到

m_ids列表记录着形成快照的时,活跃的事务ID
- 如果记录中的DB_TRX_ID,和
m_up_limit_id,即m_ids中最小的事务ID作比较,小于这个事务ID,代表该事务一定已经提交,其记录一定是历史数据,可以读取 - 如果记录中的DB_TRX_ID,等于
m_creator_trx_id,代表是自己修改的数据,可以读取 - 如果记录中的DB_TRX_ID,在
m_ids中,代表修改该记录的事务还未提交或在形成快照后才提交,不可读取 - 如果记录中的DB_TRX_ID,大于等于
m_low_limit_id,即在快照形成时,系统还未分配的事务ID,代表该数据是在快照形成后才形成的,不可读取
如果查找不应该看的版本,可以按照回滚指针,跳转到上一个历史版本,直到符合条件
模拟Read View过程
假设当前有记录;
| name | age | DB_TRX_ID | DB_ROW_ID | DB_ROLL_PTR |
|---|---|---|---|---|
| 张三 | 28 | null | 1 | null |
目前不关心创建该记录的事务ID,并且因为是创建的记录,所以没有历史版本,所以回滚指针为null
事务操作:
| 事务1[id=1] | 事务2[id=2] | 事务3[id=3] | 事务4[id=4] |
|---|---|---|---|
| begin | begin | begin | begin |
| … | … | … | 修改且提交 |
| 进行中 | 快照读 | 进行中 | |
| … | … | … |
事务4:修改name(张三)变成name(李四)
当事务2对某行数据进行快照读时,数据库会为该行数据生成一个Read View读视图
事务2的Read View
m_ids:1,3
up_limit_id:1
low_limit_id:4+1=5,读视图生成时,系统尚未分配的下一个事务ID
creator_trx_id:2
此时的版本链如下:

因为事务4在事务2形成快照前就提交了,所以是可见的
事务2在快照读时,就会拿该记录的DB_TRX_ID跟Read View中的几个属性比较,判断该版本是否可见
比较步骤
DB_TRX_ID(4)< up_limit_id(1)? 不小于,下一步
DB_TRX_ID(4)>= low_limit_id(5) ? 不大于,下一步
m_ids.contains(DB_TRX_ID) ? 不包含,说明事务4不在当前的活跃事务中。
四. RC与RR的本质区别
RC即Read Committed(读提交),RR即Repeatable Read(可重复读)
详细定义可见【MySQL】事务
Read View生成时机的不同,从而造成RC,RR级别下快照读的结果不同
RR级别的快照读
在RR级别下的某个事务对某条记录的第一次快照读会创建一个快照和Read View,将当前系统活跃的其他事务记录起来
之后再快照读时,还是使用同一个Read View,所以只要当前事务在其他事务提交之前使用过快照读,那么之后的快照读使用的都是同一个Read View,对之后的修改不可见
即在RR界别下,快照读生成Read View时,Read View会记录此时所有其他活动事务的快照,这些事务的修改对于当前事务都是不可见的,而早于Read View创建的事务所做的修改均是可见的
RC级别的快照读
在RC级别下,事务没词快照读都会新生成一个快照和Read View,所以即使后来的事务提交了,其修改结果也可见,因为RC级别下的Read View是每次快照读都会新形成的
RC级别下的Read View是每次快照读都会新形成,而RR级别的Read View只会在第一次快照读时形成
推荐文章
【MySQL笔记】正确的理解MySQL的MVCC及实现原理
详细分析MySQL事务日志(redo log和undo log)
【MySQL】InnoDB 如何避免脏读和不可重复读
结束语
感谢看到此处
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。

相关文章:
【MySQL】MVCC机制(undo log,read view)
文章目录 前言一. 预备知识二. 模拟MVCC三. Read View四. RC与RR的本质区别结束语 前言 MVCC(多版本并发控制)是一种用来解决读-写冲突的无锁并发控制 MVCC为事务分配单向增长的事务ID,为每个修改保存一个版本,版本与事物ID相关联…...
gma 2 教程(三)坐标参考系统:3.投影方法
安装 gma:pip install gma 地图投影是利用一定数学法则把地球表面的经、纬线转换到平面上的理论和方法。由于地球是一个赤道略宽两极略扁的不规则的梨形球体,故其表面是一个不可展平的曲面,所以运用任何数学方法进行这种转换都会产生误差和变…...
蓝桥杯每日一题2023.11.2
题目描述 等差素数列 - 蓝桥云课 (lanqiao.cn) 题目分析 对于此题我们需要求出最小的公差并且长度为10, 1.确保序列开始为素数 2.确定枚举的个数 注意:序列中数只是d的变化,可以通过此计算将开始数字后9个数字都计算出来,d是…...
Leetcode67二进制求和
1104 代码: class Solution {public String addBinary(String a, String b) {StringBuffer ans new StringBuffer();int n Math.max(a.length(),b.length()),carry 0;for(int i0;i<n;i){carry i < a.length()?(a.charAt(a.length()-1-i)-0):0;carry i…...
线性代数 第五章 特征值与特征向量
一、特征值定义 二、特征值求法 定义法;;相似。 三、特征向量求法 定义法;基础解系法;;相似。 四、特征值性质 不同特征值的特征向量线性无关k重特征值至多有k个线性无关的特征向量 五、相似的定义 若ÿ…...
Python嵌入式数据库 / 轻量级数据库 / 小型数据库介绍(SQLite、Pandas DataFrame、TinyDB)(python数据库)
文章目录 Python嵌入式数据库/轻量级数据库介绍什么是嵌入式数据库/轻量级数据库?SQLitePandasTinyDB总结 Python嵌入式数据库/轻量级数据库介绍 在构建应用程序时,数据存储是必不可少的一部分。传统的方式是使用如MySQL、PostgreSQL这样的重量级数据库…...
USB PD v1.0快速充电通信原理
1 原理 本篇文章讲的快速充电是指USB论坛所发布的USB Power Delivery快速充电规范(通过VBUS直流电平上耦合FSK信号来请求充电器调整输出电压和电流的过程),不同于本人发布的另一篇文章所讲的高通Quick Charger 2.0规范,因为高通QC…...
【华为】路由器以PPPoE拨号接入广域网
组网需求 用户希望以PPPoE拨号方式接入广域网,如图1所示,Router作为PPPoE客户端,得到PPPoE服务器的认证后获得IP地址,实现用户接入互联网的需求。内网网关地址(即VLANIF1接口的IP地址)为10.137.32.1/24。 …...
Linux内核分析(一)--内核架构和子系统
目录 一、引言 二、内核架构 ------>2.1、kernel源码获取 ------>2.2、cpuinfo ------>2.3、内核体系结构 ------>2.4、内核主要组件 三、内核源码及子系统 ------>3.1、整体结构与子系统 ------>3.2、cpuinfo ------>3.3、整体结构与子系统 -…...
【PyQt学习篇 · ⑨】:QWidget -控件交互
文章目录 是否可用是否显示/隐藏是否编辑是否为活跃窗口关闭综合案例信息提示状态提示工具提示“这是什么”提示 焦点控制单个控件角度父控件角度 是否可用 setEnabled(bool):该函数用于设置QWidget控件的可用性,参数bool为True表示该控件为可用状态&…...
新版本IntelliJ IDEA(如2023)中运行Spring Boot找不到VM options进行端口的修改的问题解决
问题 如下图找不到VM options。 解决 进行如下操作即可。...
Swift语言配合HTTP写的一个爬虫程序
下段代码使用Embassy库编写一个Swift爬虫程序来爬取jshk的内容。我会使用proxy_host为duoip,proxy_port为8000的爬虫IP服务器。 使用Embassy库编写一个Swift爬虫程序可以实现从网页上抓取数据的功能。下面是一个简单的步骤: 1、首先,需要在X…...
【lvgl】linux开发板搭建环境
前言 本章介绍如何在linux开发板准备好了fb0的情况下移植lvgl。 抓取源码 git clone https://github.com/lvgl/lvgl.git git clone https://github.com/lvgl/lv_drivers.git git clone https://github.com/lvgl/lv_demos.git git clone https://github.com/lvgl/lv_port_lin…...
C之(10)CMocka-单元测试框架使用
CMocka基础使用 Author:Once Day Date:2023年6月15日 参考文档: GoogleTest User’s Guide | GoogleTest嵌入式自动化单元测试(2)-Cmocka - 知乎 (zhihu.com)使用 cmocka 进行单元测试 | 前尘逐梦 (qianchenzhumeng.github.io)cmocka - un…...
如何在idea中使用maven搭建tomcat环境
目录 一、创建maven项目 二、完善代码结构 三、引入依赖和插件 四、启动tomcat,运行项目 (1)点击添加配置 (2)点击左上角的加号,选择maven (3)输入运行命令 五、验证 一、创建…...
单点登录
单点登录(Single Sign-On,简称SSO)是一种可以让用户在多个应用程序和网站中使用同一组登录凭证的技术,即用户只需通过一次身份验证,就可以访问多个应用和网站。以下是单点登录的相关知识点: 1. 身份验证机…...
大数据(十):数据可视化(二)
专栏介绍 结合自身经验和内部资料总结的Python教程,每天3-5章,最短1个月就能全方位的完成Python的学习并进行实战开发,学完了定能成为大佬!加油吧!卷起来! 全部文章请访问专栏:《Python全栈教…...
pytorch+LSTM实现使用单参数预测,以及多参数预测(代码注释版)
开发前准备: 环境管理:Anaconda python: 3.8 显卡:NVIDIA3060 pytorch: 到官网选择conda版本,使用的是CUDA11.8 编译器: PyCharm 简述: 本次使用seaborn库中的flights数据集来做试验,我们通过…...
腾讯云3年/5年特惠云服务器购买入口及攻略
腾讯云是腾讯旗下云计算品牌,为了吸引用户经常推出各种优惠活动,最吸引用户的还是特惠云服务器,下面给大家分享腾讯云3年/5年时长特惠服务器购买入口及教程! 购买入口:传送门>>> 购买攻略: 进入…...
【Linux】jdk Tomcat MySql的安装及Linux后端接口部署
一,jdk安装 1.1 上传安装包到服务器 打开MobaXterm通过Linux地址连接到Linux并登入Linux,再将主机中的配置文件复制到MobaXterm 使用命令查看:ll 1.2 解压对应的安装包 解压jdk 解压命令:tar -xvf jdk 加键盘中Tab键即可…...
ESXi 8.0 无法选择分区方式 小白级详细解决办法
本文针对 ESXi 8.0 安装 / 使用中无法选择分区方式、看不到分区选项、分区界面灰掉、提示分区不支持等问题,从根源排查到终极修复,全程纯文字、步骤拆解到最小操作,小白照着做就能解决,无任何表格。一、先明确:什么是 …...
MySQL 8.0 等保合规实战:手把手配置开源审计插件 server_audit.so
MySQL 8.0 等保合规审计插件实战指南 在数字化转型浪潮中,数据库安全审计已成为企业合规运营的刚需。对于使用MySQL 8.0的企业而言,如何在不影响性能的前提下满足等保2.0三级及以上对数据库审计的要求,是每位DBA和安全工程师必须掌握的技能。…...
提升arduino开发效率:用快马平台一键生成常用工具模块代码
作为一名经常折腾Arduino的开发者,我发现在项目开发中,总有些重复性的代码需要反复编写。最近尝试用InsCode(快马)平台来生成这些常用工具模块,效率提升非常明显。今天就把我的实践心得分享给大家。 I2C设备扫描功能 在连接多个I2C设备时&…...
终极GPU显存检测指南:使用memtest_vulkan轻松诊断显卡稳定性问题
终极GPU显存检测指南:使用memtest_vulkan轻松诊断显卡稳定性问题 【免费下载链接】memtest_vulkan Vulkan compute tool for testing video memory stability 项目地址: https://gitcode.com/gh_mirrors/me/memtest_vulkan 显卡显存稳定性直接影响着游戏体验…...
MCP2518FD屏蔽寄存器自动配置算法(11bit标准帧多ID接收场景)
1. 为什么需要自动配置屏蔽寄存器? 在CAN总线通信中,MCP2518FD作为一款常用的CAN控制器,经常需要处理多ID接收的场景。想象一下你正在开发一个汽车电子控制单元(ECU),需要同时接收来自发动机、变速箱、ABS等多个模块的数据。每个…...
解锁戴森电池3大突破:固件破解技术让32次红灯故障电池重生
解锁戴森电池3大突破:固件破解技术让32次红灯故障电池重生 【免费下载链接】FU-Dyson-BMS (Unofficial) Firmware Upgrade for Dyson V6/V7 Vacuum Battery Management System 项目地址: https://gitcode.com/gh_mirrors/fu/FU-Dyson-BMS 当你的戴森吸尘器突…...
全栈实战应用:基于快马AI快速构建带投稿审稿系统的《构石》期刊官网
全栈实战应用:基于快马AI快速构建带投稿审稿系统的《构石》期刊官网 最近接手了一个学术期刊官网的开发需求,需要实现完整的在线投稿和审稿流程。这个项目涉及前后端联调和数据库设计,正好可以试试用InsCode(快马)平台来快速搭建原型。下面分…...
快手投放的困局:计划搭建占80%时间,人效去哪了?
25人的代理商团队,10个人专职建计划,每天点鼠标点到手麻。换了种做法后,2个人2小时搞定1000条计划。他们做对了什么? 01 为什么快手投放这么累? 做快手投放的朋友,尤其是服务多个客户的代理商,…...
如何彻底掌控Windows Defender:终极禁用与启用指南
如何彻底掌控Windows Defender:终极禁用与启用指南 【免费下载链接】defender-control An open-source windows defender manager. Now you can disable windows defender permanently. 项目地址: https://gitcode.com/gh_mirrors/de/defender-control 你是…...
别再让死区拖后腿!用MATLAB Simulink给SVPWM逆变器做个精准‘补偿手术’
电力电子工程师实战:用MATLAB Simulink实现SVPWM逆变器的死区补偿 在电力电子系统设计中,死区效应就像电路板上的隐形杀手——它不会立即摧毁你的系统,却会悄无声息地降低整体性能。作为一名长期奋战在电机控制一线的工程师,我见过…...
