MySQL的InnoDB引擎的事务原理以及MVCC

news2024/11/30 3:29:22

目录

一、事务原理

二、redo log

三、undo log

四、MVCC

        1.基础概念

        2.隐藏字段

        3.undolog

        4.readview

        5.原理分析


一、事务原理

        1). 事务

        事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

        2). 特性

        • 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。

        • 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。

        • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。

        • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

那实际上,我们研究事务的原理,就是研究MySQL的InnoDB引擎是如何保证事务的这四大特性的。

        而对于这四大特性,实际上分为两个部分。 其中的原子性、一致性、持久化,实际上是由InnoDB中的两份日志来保证的,一份是redo log日志,一份是undo log日志。 而持久性是通过数据库的锁,加上MVCC来保证的。

        我们在讲解事务原理的时候,主要就是来研究一下redolog,undolog以及MVCC。

二、redo log

        重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。

        该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中, 用于在刷新脏页到磁盘,发生错误时, 进行数据恢复使用。

        如果没有redo log,可能会存在什么问题的? 我们一起来分析一下。

        我们知道,在InnoDB引擎中的内存结构中,主要的内存区域就是缓冲池,在缓冲池中缓存了很多的数据页。 当我们在一个事务中,执行多个增删改的操作时,InnoDB引擎会先操作缓冲池中的数据,如果缓冲区没有对应的数据,会通过后台线程将磁盘中的数据加载出来,存放在缓冲区中,然后将缓冲池中的数据修改,修改后的数据页我们称为脏页。 而脏页则会在一定的时机,通过后台线程刷新到磁盘中,从而保证缓冲区与磁盘的数据一致。 而缓冲区的脏页数据并不是实时刷新的,而是一段时间之后将缓冲区的数据刷新到磁盘中,假如刷新到磁盘的过程出错了,而提示给用户事务提交成功,而数据却没有持久化下来,这就出现问题了,没有保证事务的持久性。

        那么,如何解决上述的问题呢? 在InnoDB中提供了一份日志 redo log,接下来我们再来分析一下,通过redo log如何解决这个问题。

        有了redo log之后,当对缓冲区的数据进行增删改之后,会首先将操作的数据页的变化,记录在redo log buffer中。在事务提交时,会将redo log buffer中的数据刷新到redo log磁盘文件中。过一段时间之后,如果刷新缓冲区的脏页到磁盘时,发生错误,此时就可以借助于redo log进行数据恢复,这样就保证了事务的持久性。 而如果脏页成功刷新到磁盘 或 或者涉及到的数据已经落盘,此时redolog就没有作用了,就可以删除了,所以存在的两个redolog文件是循环写的。

        那为什么每一次提交事务,要刷新redo log到磁盘中呢,而不是直接将buffer pool中的脏页刷新到磁盘呢 ?

        因为在业务操作中,我们操作数据一般都是随机读写磁盘的,而不是顺序读写磁盘。 而redo log在往磁盘文件中写入数据,由于是日志文件,所以都是顺序写的。顺序写的效率,要远大于随机写。 这种先写日志的方式,称之为 WAL(Write-Ahead Logging)。

三、undo log

        回滚日志,用于记录数据被修改前的信息 , 作用包含两个 : 提供回滚(保证事务的原子性) 和MVCC(多版本并发控制) 。

        undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。

        Undo log销毁:undo log在事务执行时产生,事务提交时,并不会立即删除undo log,因为这些日志可能还用于MVCC。

        Undo log存储:undo log采用段的方式进行管理和记录,存放在前面介绍的 rollback segment回滚段中,内部包含1024个undo log segment。

四、MVCC

        1.基础概念

        1). 当前读

        读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select ... lock in share mode(共享锁),select ... for update、update、insert、delete(排他锁)都是一种当前读。

        测试:

        在测试中我们可以看到,即使是在默认的RR隔离级别下,事务A中依然可以读取到事务B最新提交的内容,因为在查询语句后面加上了 lock in share mode 共享锁,此时是当前读操作。当然,当我们加排他锁的时候,也是当前读操作。

        2). 快照读

        简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。

        • Read Committed:每次select,都生成一个快照读。

        • Repeatable Read:开启事务后第一个select语句才是快照读的地方。

        • Serializable:快照读会退化为当前读。

        测试:

        在测试中,我们看到即使事务B提交了数据,事务A中也查询不到。 原因就是因为普通的select是快照读,而在当前默认的RR隔离级别下,开启事务后第一个select语句才是快照读的地方,后面执行相同的select语句都是从快照中获取数据,可能不是当前的最新数据,这样也就保证了可重复读。

        2.隐藏字段

        当我们创建了上面的这张表,我们在查看表结构的时候,就可以显式的看到这三个字段。 实际上除了这三个字段以外,InnoDB还会自动的给我们添加三个隐藏字段及其含义分别是:

        而上述的前两个字段是肯定会添加的, 是否添加最后一个字段DB_ROW_ID,得看当前表有没有主键,如果有主键,则不会添加该隐藏字段。

        3.undolog

        回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。而update、delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。

有一张表原始数据为:

        DB_TRX_ID : 代表最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID,是自增的。

        DB_ROLL_PTR : 由于这条数据是才插入的,没有被更新过,所以该字段值为null。

        然后,有四个并发事务同时在访问这张表。

        A. 第一步

        当事务2执行第一条修改语句时,会记录undo log日志,记录数据变更之前的样子; 然后更新记录,并且记录本次操作的事务ID,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本。

        B.第二步

        当事务3执行第一条修改语句时,也会记录undo log日志,记录数据变更之前的样子; 然后更新记录,并且记录本次操作的事务ID,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本。

        C. 第三步

        当事务4执行第一条修改语句时,也会记录undo log日志,记录数据变更之前的样子; 然后更新记录,并且记录本次操作的事务ID,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本。

        最终我们发现,不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。

        4.readview

        ReadView(读视图)是 快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。

        ReadView中包含了四个核心字段:

        而在readview中就规定了版本链数据的访问规则:

        trx_id 代表当前undolog版本链对应事务ID。

        不同的隔离级别,生成ReadView的时机不同:

                READ COMMITTED :在事务中每一次执行快照读时生成ReadView。

                REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。

        5.原理分析

        RC隔离级别:

        RC隔离级别下,在事务中每一次执行快照读时生成ReadView。

        我们就来分析事务5中,两次快照读读取数据,是如何获取数据的?

        在事务5中,查询了两次id为30的记录,由于隔离级别为Read Committed,所以每一次进行快照读都会生成一个ReadView,那么两次生成的ReadView如下。

        那么这两次快照读在获取数据时,就需要根据所生成的ReadView以及ReadView的版本链访问规则,到undolog版本链中匹配数据,最终决定此次快照读返回的数据。

        A. 先来看第一次快照读具体的读取过程:

        在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:

        B. 再来看第二次快照读具体的读取过程:

        在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:

        RR隔离级别

        RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。 而RR 是可重复读,在一个事务中,执行两次相同的select语句,查询到的结果是一样的。

        那MySQL是如何做到可重复读的呢? 我们简单分析一下就知道了

        我们看到,在RR隔离级别下,只是在事务中第一次快照读时生成ReadView,后续都是复用该ReadView,那么既然ReadView都一样, ReadView的版本链匹配规则也一样, 那么最终快照读返回的结果也是一样的。

        所以呢,MVCC的实现原理就是通过 InnoDB表的隐藏字段、UndoLog 版本链、ReadView来实现的。而MVCC + 锁,则实现了事务的隔离性。 而一致性则是由redolog 与 undolog保证。

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

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

相关文章

k8s下搭建redis集群

记录一下近期实现的在k8s上搭建redis集群的过程 1、新建存储类 主要是为了和其它服务的存储类区分一下 redis-beta-storage 2、编写configMap redis启动时从configMap中读取配置 bind:默认的127.0.0.1可能会导致其它ip地址无法远程访问,因此修改为0.0…

【软件工程导论】——Visio与StarUML的安装

目录 🕒 1. Visio🕒 2. StarUML 🕒 1. Visio 1、下载Office Tool Plus并安装:🔎 Office Tool Plus官网 2、打开软件 → 部署 → 添加产品 3、这里我选择Visio 2021 专业版 LTSC,确定,随后点击“…

【RedHat】使用cron安排周期性任务——周期性创建用户实例

cron用来管理周期性重复执行的任务调度,非常适合日常系统维护工作。计划任务分为系统的计划任务和用户自定义的计划任务。 cron服务每分钟都检查/etc/crontab文件、/etc/cron.d目录和/var/spool/cron目录中的变化。/var/spool/cron目录下的任务需要通过crontab -e 命…

鸿蒙原生应用开发-网络管理HTTP数据请求

一、场景介绍 应用通过HTTP发起一个数据请求,支持常见的GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT方法。 二、接口说明 HTTP数据请求功能主要由http模块提供。 使用该功能需要申请ohos.permission.INTERNET权限。 涉及的接口如下表,具体的…

MSTP环路避免实验(思科)

华为设备参考:MSTP环路避免实验(华为) 一,技术简介 MSTP(多生成树协议),MSTP解决了STP和RSTP没有考虑vlan的问题,STP和RSTP将所有的vlan共享为一个生成树实例,无法实现…

如何备考2024年AMC10:吃透2000-2023年1250道真题(限时免费送)

有家长朋友问,有没有适合初中学生参加的奥数类比赛?我推荐AMC10美国数学竞赛,在国内可以方便地参加,而且每年全国各省市参加的初中生越来越多。关于AMC10详细的介绍和常见问题解答,可以联系我获得。 那么如何在AMC10竞…

基恩士数码显微镜数据采集项目经验分享

在最近的项目中,我有幸参与了基恩士数码显微镜的开发与数据采集工作。这次的经历让我对科学研究中的数据收集和分析有了更深入的了解,我分享我在项目中的角色以及我们所取得的成果。 作为一个科研工作者,我始终被数字化技术在科学研究中的作用…

消息队列经典应用场景

笔者心中,消息队列,缓存,分库分表是高并发解决方案三剑客。 在职业生涯中,笔者曾经使用过 ActiveMQ 、RabbitMQ 、Kafka 、RocketMQ 这些知名的消息队列 。 这篇文章,笔者结合自己的真实经历,和大家分享消息队列的七种经典应用场景。 1 异步&解耦 笔者曾经负责某电…

开源AI引擎:利用影像处理与目标检测技术对违章建筑排查

一、项目案例介绍 随着城市化进程的加快,城市规划和管理工作面临着前所未有的挑战,违章建筑的排查与处理成为了城市管理中的一项重要任务。传统的违章建筑排查方法依赖于人力巡查,效率低下且难以全面覆盖。为了解决这一问题,现代…

【Win】使用PowerShell和Webhooks轻松发送消息至Microsoft Teams

Microsoft Teams是一款由微软开发的团队协作和通讯工具。如果您对这个名字还不太熟悉,那么现在就是一个了解它的好时机。微软将Teams定位为其之前Skype for Business解决方案的继任者,并且它也提供了与其他基于频道的通讯应用程序(例如Slack、…

HarmonyOs开发:轮播图Banner组件封装与使用

前言 轮播图在每个项目中都很常见,鸿蒙中在容器组件中也提供了Swiper组件,用于子组件滑动轮播显示,和前端的使用起来也是异曲同工,我们先看下基本的用法。 Swiper() {ForEach(["1", "2", "3", &quo…

C语言循环结构的程序设计

在C语言中,循环结构是一种重要的控制结构,用于重复执行特定的代码块,直到满足特定的条件为止。循环结构使得程序可以更加灵活和高效地处理重复性的任务,从而提高了程序的可读性和可维护性。本文将深入介绍C语言中循环结构的程序设…

C++王牌结构hash:哈希表开散列(哈希桶)的实现与应用

目录 一、开散列的概念 1.1开散列与闭散列比较 二、开散列/哈希桶的实现 2.1开散列实现 哈希函数的模板构造 哈希表节点构造 开散列增容 插入数据 2.2代码实现 一、开散列的概念 开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址&…

canvas跟随鼠标画有透明度的椭圆边框

提示&#xff1a;canvas跟随鼠标画有透明度的椭圆边框 文章目录 前言一、跟随鼠标画有透明度的椭圆边框总结 前言 一、跟随鼠标画有透明度的椭圆边框 test.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8">&…

Web APIs知识点讲解(阶段五)

DOM- 网页特效篇 一.课前回顾(手风琴) <!DOCTYPE html> <html><head lang"en"><meta charset"UTF-8"><title>手风琴</title><style>ul {list-style: none;}* {margin: 0;padding: 0;}div {width: 1200px;heig…

【过度拟合?秒了!】

目录 引言 一、简化模型复杂度 1 .1 特征选择 1.2 降低多项式阶数 1.3 减少神经元数量或层数 二、使用正则化技术 2.1 L1正则化&#xff08;Lasso&#xff09; 工作原理 应用场景 2.2 L2正则化&#xff08;Ridge&#xff09; 2.3 Elastic Net正则化 2.4 代码事例 …

同是3D国漫公司,唯独这家,建模堪称国内天花板?

随着动画《斗罗大陆》播放量破500亿&#xff0c;一脚踹开国漫市场的大门&#xff0c;3D国漫开始迈入“内卷”新阶段。 &#xff08;图&#xff1a;斗罗大陆&#xff1a;双神战双神&#xff09; 同时热门网文改编成为当下国产动画公司的基本选项&#xff0c;包括“双斗”&#…

37-巩固练习(一)

37-1 if语句等 1、问&#xff1a;输出结果 int main() {int i 0;for (i 0; i < 10; i){if (i 5){printf("%d\n", i);}return 0;} } 答&#xff1a;一直输出5&#xff0c;死循环 解析&#xff1a;i5是赋值语句&#xff0c;不是判断语句&#xff0c;每一次循…

redis 保存是否可以更快?

redis 常见用法之保存 在java项目很多人都喜欢用spring-boot-starter-data-redis下的StringRedisTemplate操作redis,大多项目也用作为缓存&#xff0c;他们最常见的保存key value代码&#xff0c;如下&#xff1a; stringRedisTemplate.opsForValue().set(key, value); 大家都…

蓝桥杯算法题练习

1、20世纪有多少个星期一 &#xff08;1901、1、1——2000、12、31&#xff09; 方法一&#xff1a;python代码 方法二&#xff1a;excel工具(设置单元格格式&#xff0c;把日期换成周几的形式) 2、100个数相乘&#xff0c;结果有几个0 3、切面条 找规律:对折次数n 弯2^n-1 面…