【MySQL】MVCC机制(undo log,read view)

news2024/11/15 21:56:45

文章目录

  • 前言
  • 一. 预备知识
  • 二. 模拟MVCC
  • 三. Read View
  • 四. RC与RR的本质区别
  • 结束语

前言

MVCC(多版本并发控制)是一种用来解决读-写冲突的无锁并发控制

MVCC为事务分配单向增长的事务ID,为每个修改保存一个版本,版本与事物ID相关联,读操作只读该事务开始前的数据库的快照,所以MVCC可以解决以下问题

  • 在并发读写数据库数据时,读操作不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写性能
  • 同时解决脏读,幻读,不可重复读等事务隔离性问题,但不能解决更新丢失问题

数据库并发的场景

数据库并发场景有三种

  1. 读-读:不存在任何问题,不需要并发控制,但有使用共享锁
  2. 读-写:有线程安全问题,可能会造成事务隔离性问题,如脏读,幻读,不可重复读
  3. 写-写:有线程安全问题,可能会存在更新丢失问题,如第一类丢失更新问题和第二类更新丢失问题

补充

第一类更新丢失问题(回滚丢失,Lost update)

第一类更新丢失是指,一个事务被撤销,可能导致其他事务已提交的更新数据被覆盖

时间序号事务一事务二
T1begin开启事务begin开启事务
T2查询余额money=1000查询余额money=1000
T3存款100,money=1100
T4取款100,money=900
T5commit提交事务
T6回滚取款操作,money恢复1000

正如上述事例,事务一二开始查询余额都是1000,事务二先进行存款操作,并提交。
事务一不知道事务二的存在,进行取款操作,但是又进行了回滚,就会将余额恢复成最开始查询的数额,这就覆盖了事务二的更新操作

第二类更新丢失问题(覆盖丢失/两次更新问题,Second lost update)

第二类更新丢失是指,当两个事物或多个事务查询相同的记录,然后各自基于查询结果更新数据

时间序号事务一事务二
T1begin开启事务begin开启事务
T2查询余额money=1000查询余额money=1000
T3取款100,money=900
T4commit提交事务
T5存款100,money=1100
T6commit提交事务

事务一二查询相同余额=1000,事务二先进行取款操作,money=900,但事务一后续基于自己的查询结果,进行存款操作,money=1100,这就覆盖了事务二的数据更新

一. 预备知识

学习MVCC前,我们要有以下三个知识了解

  • 3个记录隐藏字段
  • undo日志(undo log)
  • Read View

3个记录隐藏字段

这3个字段是记录信息

  • DB_TRX_ID:6byte。最近修改该记录的事务ID,记录创建这条记录/最后一次修改改记录的事务ID
  • DB_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个隐藏字段

nameageDB_TRX_IDDB_ROW_IDDB_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是逻辑日志,实现事务的原子性

  1. undo log记录的是事务开始前的数据状态,记录的是更新前的值
  2. undo log实现事务的原子性提供回滚

redo log是物理日志,实现事务的持久性

  1. redo log记录的是事务完成后的数据状态,记录的是更新后的值
  2. 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读取就被分为了两种读:

  1. 快照读,读取历史版本
  2. 当前读,读取最新数据,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

  1. 如果记录中的DB_TRX_ID,和m_up_limit_id,即m_ids中最小的事务ID作比较,小于这个事务ID,代表该事务一定已经提交,其记录一定是历史数据可以读取
  2. 如果记录中的DB_TRX_ID,等于m_creator_trx_id,代表是自己修改的数据可以读取
  3. 如果记录中的DB_TRX_ID,在m_ids中,代表修改该记录的事务还未提交或在形成快照后才提交不可读取
  4. 如果记录中的DB_TRX_ID,大于等于m_low_limit_id,即在快照形成时,系统还未分配的事务ID,代表该数据是在快照形成后才形成的,不可读取

如果查找不应该看的版本,可以按照回滚指针,跳转到上一个历史版本,直到符合条件


模拟Read View过程

假设当前有记录;

nameageDB_TRX_IDDB_ROW_IDDB_ROLL_PTR
张三28null1null

目前不关心创建该记录的事务ID,并且因为是创建的记录,所以没有历史版本,所以回滚指针为null

事务操作:

事务1[id=1]事务2[id=2]事务3[id=3]事务4[id=4]
beginbeginbeginbegin
修改且提交
进行中快照读进行中

事务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 如何避免脏读和不可重复读

结束语

感谢看到此处
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

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

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

相关文章

043-第三代软件开发-第三方串口库使用

第三代软件开发-第三方串口库使用 文章目录 第三代软件开发-第三方串口库使用项目介绍第三方串口库使用示例代码 关键字&#xff1a; Qt、 Qml、 QextSerialPort、 QThread、 高频 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt…

Websocket传输协议是什么

WebSocket 是一种网络通信协议&#xff0c;属于 HTML5 规范的一部分。它提供了在单个长期连接上进行全双工通信的能力&#xff0c;使得数据可以从客户端发送到服务器&#xff0c;也可以从服务器发送到客户端&#xff0c;这与传统的 HTTP 请求和响应模型不同。 WebSocket 协议定…

ROS Hello World

万物始于Hello World&#xff0c;为了体验ROS&#xff0c;使用Hello World介绍ROS的简单使用。 一、Hello World工程简介 首先需要创建工程&#xff0c;流程为&#xff1a; 创建工作空间目录&#xff08;即工程根目录&#xff0c;注意此时还不是ROS工作空间&#xff0c;只是…

>LINK : fatal error LNK1561和LINK : fatal error LNK1168:解决方法

>LINK : fatal error LNK1561和LINK : fatal error LNK1168:解决方法 >LINK : fatal error LNK1561和LINK : fatal error LNK1168:解决方法_fatal link error-CSDN博客 如果无法解决&#xff1a;只能试试在之前的项目中能否运行 这个错误提示表明在编译连接时&#xff…

PubScholar-可检索1.7亿篇科技文献的公益平台来了!

可检索1.7亿篇科技文献的 公益平台来了&#xff01; 11月1日 由中国科学院等单位联合建设的 PubScholar公益学术平台 正式对社会公众开放 网址&#xff1a;https://pubscholar.cn/ “公益学术平台”旨在为我国科技界和全社会提供高质量的公益性学术资源&#xff0c;提供学…

安全第一!速卖通测评补单稳定的系统注意事项大盘点

对新卖家而言&#xff0c;测评并非可耻之事&#xff0c;反而是无法起步、耗费自身时间才是真正的可耻。由于速卖通新店几乎无法获得任何活动的支持&#xff0c;流量也基本没有&#xff0c;因此要在90天内达成60单的业绩对于许多卖家来说都是一项挑战。因此&#xff0c;通过快速…

算法训练 第五周

一、多数元素 本题给了我们一个数组&#xff0c;要求我们找出这个数组中出现次数大于这个数组元素总量一半的那个元素&#xff0c;也可以理解为找出数组中出现次数最多的那个元素&#xff0c;本题的解决方法有很多&#xff0c;在此我们主要讨论三种解决思路。 1.Hash表 我们可…

Linux驱动——并发与竞态

并发 并发指多个执行单元同时、并行被执行&#xff0c;而并发执行单元对共享资源&#xff08;硬件资源和软件上的全局变量、静态变量等&#xff09;的访问很容易导致竞态。 如下列AB写&#xff0c;C读&#xff0c;AB在写的过程中&#xff0c;C读的话就会出错。 对称多处理器…

Lightdb23.4 Client 包含ecpg可执行程序及相关库文件

功能介绍 部分客户在使用Lightdb client绿色包时需要ecpg程序和ecpg相关的头文件和库文件&#xff0c;所以在Lightdb 23.4版本client绿色包中新增了ecpg的程序和相关头文件和库文件&#xff0c;以方便用户的使用。 Client包目录结构 bin目录是可执行程序和脚本&#xff0c;i…

windows wsl使用,安装ubuntu

windows wsl使用 环境配置windows 家庭版 打开 hyper11 安装ubuntuwsl 命令 环境配置 搜索 启动和关闭 Windows功能 打开下面组件 windows 家庭版 打开 hyper11 解决Windows11 Home 没有 Hyber-v 创建 hyber-v.cmd 并管理员身份执行 pushd "%~dp0" dir /b %Sy…

使用QEMU启动uboot引导linux内核

上篇文章中实现了使用qemu启动uboot&#xff0c;本文实现使用qemu启动uboot引导内核的过程。 一、环境准备 主机系统&#xff1a;WSL-ubuntu20.04 uboot版本&#xff1a;u-boot-2023.10 Kernel版本&#xff1a;linux-5.4.18 二、制作sd卡 qemu支持模拟sd卡&#xff0c;可以…

Linux C语言进阶-D11多级指针、void指针及const

多级指针 多级指针变量&#xff1a;指向指针变量的指针变量 在下图中&#xff0c;定义一个a数组&#xff0c;再定义一个指针数组p[2]&#xff0c;其中p数组中存储的是地址&#xff0c;再定义一个二级指针q指向p[0]&#xff0c;即&p[0]&#xff0c;又由于&p[0]就是p&am…

SpringCloud(七) Feign远程调用

目录 一, RestTemplate远程调用存在的问题 二, Feign的远程调用 2.1 什么是Fegin 2.2 Feign的使用(代替RestTemplate) 1. 引入依赖 2. 添加注解 3. 编写Feign的客户端 4. 测试 5. 总结 2.3 自定义配置 1. 配置文件方式 2. Java代码方式 三, Feign使用优化 3.…

keepalived与nginx与MySQL

keepalived VRRP介绍 集群&#xff08;cluster&#xff09;技术是一种较新的技术&#xff0c;通过集群技术&#xff0c;可以在付出较低成本的情况下获得在性能、可靠性、灵活性方面的相对较高的收益&#xff0c;其任务调度则是集群系统中的核心技术。 集群组成后&#xff0c;可…

Jellyfin转码和色调映射效率提升:开启处理器低电压模式(GuC/HuC)

Jellyfin转码和色调映射效率提升 系统环境拉取nyanmisaka/jellyfin镜像开启低电压模式Jellyfin配置开启QSV和低电压模式解码开启色调映射&#xff08;基于OpenCL&#xff09; 实测验证低电压模式转码帧率实测测试视频概况221029版本 vs 230901版本开启色调映射的影响开启低电压…

AI智能分析网关高空抛物算法如何实时检测高楼外立面剥落?

高楼外立面剥落是一种十分危险的行为&#xff0c;会造成严重的人身伤害和财产损失。TSINGSEE青犀智能分析网关利用高楼外立面剥落的信息&#xff0c;结合高空抛物算法来进行处理就可很好解决此问题。 1. 数据收集 首先&#xff0c;需要收集关于高楼外立面剥落的数据。这可以通…

Restful风格与Wesocket之间的关联

RESTful 风格和 WebSocket 本质上是两种不同的通信机制&#xff0c;它们在设计理念和用途上有明显的区别。 RESTful 是一种基于 HTTP 协议的架构风格&#xff0c;它使用标准的 HTTP 方法&#xff08;如 GET、POST、PUT、DELETE 等&#xff09;来进行资源的操作&#xff0c;并倾…

计算机毕业设计选题推荐-招聘信息推荐系统-Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

浅谈煤矿井下6kV动力电缆绝缘线监测系统的设计与应用

贾丽丽 安科瑞电气股份有限公司上海嘉定 201801 摘要&#xff1a;针对供电系统绝缘问题检测技术限制煤炭产量效率的问题&#xff0c;以某煤炭企业6kV井下供电系统为研究对象&#xff0c;开展了在线监测系统设计与应用工作。结果表明&#xff0c;系统工作稳定&#xff0c;满足井…

为什么开设项目管理专栏

首先&#xff0c;向各位同道同行&#xff0c;分享一个来自博主的好消息&#xff1a; 2023年8月&#xff0c;经过一次为期3个月的紧张、有序、活泼、生动的学习之旅&#xff0c;博主顺利通过了PMP认证&#xff0c;并且拿下3A。 其次&#xff0c;为记录本次学习过程的点滴和心得…