学习mysql9-undoLog

本文深入探讨了MySQL中的事务回滚机制,介绍了undoLog在事务原⼦性保证中的作用,包括insert、delete和update三种操作的undoLog格式。详细阐述了事务Id的分配时机,以及undoLog页面链表、回滚段的概念和分类。此外,还讲解了undoLog的写入过程及回滚段的相关配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事务

事务回滚

事务需要保证原⼦性,也就是事务中的操作要么全部完成,要么什么也不做。
为了保证事务的原⼦性,我们需要把东西改回原先的样⼦,这个过程就称之为回滚(英⽂名:rollback),这样就可以造成⼀个假象:这个事务看起来什么都没做,所以符合原⼦性要求。
你插⼊⼀条记录时,⾄少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。
你删除了⼀条记录,⾄少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插⼊到表中就好了。
你修改了⼀条记录,⾄少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。

事务Id

给事务分配id的时机

我们可以通过START TRANSACTION READ ONLY语句开启⼀个只读事务
在只读事务中不可以对普通的表(其他事务也能访问到的表)进⾏增、删、改操作但可以对临时表做增、删、改操作
我们可以通过START TRANSACTION READ WRITE语句开启⼀个读写事务,或者使⽤BEGIN、START TRANSACTION语句开启的事务默认也算是读写事务。
事务id和之前提到的row_id其实是一样的,都是隐藏列。每次分配到事务的时候,会维护一个全局变量,每次+1的操作。每当成为256的倍数的时候,存放在系统表空间页号为5的页面上,一个称为为Max Trx ID的属性处。

roll_pointer本质就是⼀个指针,指向记录对应的undo⽇志。表示的是回滚指针,指向具体需要回滚到的位置

undoLog的格式

insert undoLog

insertLog的格式内容比较简单,主要是因为回滚不需要知道具体怎么插入,插入了哪些内容。只需要关注插入生成的主键就可以了,每次做会滚的时候,找到对应的insert undoLog对应的Id,做一个删除就可以了。
简单的看一下格式
在这里插入图片描述
主要讲一下,undo no在⼀个事务中是从0开始递增的,主键个列信息,如果主键只有一列,这个列表只有一条。如果存在多列就存多列

delete undoLog

我们知道插⼊到页⾯中的记录会根据记录头信息中的next_record属性组成⼀个单向链表,我们把这个链表称之为正常记录链表;我们在前边唠叨数据页结构的时候说过,被删除的记录其实也会根据记录头 信息中的next_record属性组成⼀个链表,只不过这个链表中的记录占⽤的存储空间可以被重新利⽤,所以也称这个链表为垃圾链表。
在这里插入图片描述
删除某个记录的时候,可以分为两步。首先把删除标识标成1,然后把他移动到了page_free链表,之后会对已删除的数据记录做一个清理的过程。为啥需要这么做,主要是为了服务mvcc,让他能够查到那条数据,等到删除的事务提交之后,启动另外的线程去做清理,完成之后还得更新一下page_free,数据页释放空间

TRX_UNDO_DEL_MARK_REC类型的undo⽇志
在这里插入图片描述

update undoLog

不更新主键

就地更新:如果此次更新的内容和更新之前的内容,每一项的内容都和更新之前一样大,就可以不改变原来的页结构

先删除掉旧记录,再插⼊新记录:除了就地更新的情况,就是不就低更新的例子了,大了或者小了,数据库都会先把原来的那条数据直接删除,重新再插入一条新的数据。注意不是标记delete而是直接删除
如图这就是不更新主键设计的undoLog的结构:
在这里插入图片描述

更新主键

两步处理:先将旧记录进⾏delete mark操作
根据更新后各列的值创建⼀条新记录,并将其插⼊到聚簇索引中(需重新定位插⼊的位置)。
之所以只对旧记录做delete mark操作,是因为别的事务同时也可能访问这条记录,如果把它真正的删除加⼊到垃圾链表后,别的事务就访问不到了。这个功能就是所谓的 MVCC

FIL_PAGE_UNDO_LOG页⾯

有⼀种称之为FIL_PAGE_UNDO_LOG类型的页⾯是专门⽤来存储undo⽇志的,这种类型的页⾯的通⽤结构如下图 所⽰(以默认的16KB⼤⼩为例):
在这里插入图片描述
重点讲一下pageHeader,这是undoLog页面特有的header,其中的结构如图
在这里插入图片描述
type表示类型,只有两种类型可选,一种是TRX_UNDO_INSERT(使⽤⼗进制1表⽰),另外一种是TRX_UNDO_UPDATE(使⽤⼗进制2表⽰)。start表示的是该页的起始位置,也就是开始写UndoLog的位置,free表示页面中还空闲的位置,也就是写了UndoLog的截止位置,Node是一个List结构,存放的应该是其中所有UndoLog的信息

Undo页⾯链表

Undo页面在真实的场景中,一个页面应该是存放不下对应的undoLog的,所以我们提供了链表的结构,存放所有的undoLog。
单个事务的Undo页面链表,分为以下四几种:
业务表TRX_UNDO_INSERT
业务表TRX_UNDO_UPDATE
临时表TRX_UNDO_INSERT
临时表TRX_UNDO_UPDATE
在这里插入图片描述

当然这是最复杂的情况,事务刚开始的时候并不会创建这么多的链表,而是需要用到的情况下创建
多个事务的Undo页面链表
trx1和trx2同时在执行事务
在这里插入图片描述

undo⽇志具体写⼊过程

Undo Log Segment Header

每一个undoLog页面的链表都对应了一个段,这就是Undo Log Segment,每次需要申请页面都从Segment里面申请,然后加入链表。所以每个Undo Log链表的first undo page里存了一个对应段信息的Undo Log Segment Header
Undo Log Segment Header:
TRX_UNDO_STATE:本Undo⻚⾯链表处在什么状态。
TRX_UNDO_LAST_LOG:本Undo⻚⾯链表中最后⼀个Undo Log Header的位置。
TRX_UNDO_FSEG_HEADER:本Undo⻚⾯链表对应的段的Segment Header信息(对应的就是该段的segmentEntry信息)
TRX_UNDO_PAGE_LIST:Undo⻚⾯链表的基节点。这个TRX_UNDO_PAGE_LIST属性代表着这个链表的基节点,当然这个基节点只存在于Undo⻚⾯链表的第⼀个页⾯,也就是first undo page中。

Undo Log Header

在这里插入图片描述
具体对应的Undo Log Header结构
在这里插入图片描述
TRX_UNDO_TRX_ID:⽣成本组undo⽇志的事务id。
TRX_UNDO_TRX_NO:事务提交后⽣成的⼀个需要序号,使⽤此序号来标记事务的提交顺序(先提交的此序号⼩,后提交的此序号⼤)。
TRX_UNDO_DEL_MARKS:标记本组undo⽇志中是否包含由于Delete mark操作产⽣的undo⽇志。
TRX_UNDO_LOG_START:表⽰本组undo⽇志中第⼀条undo⽇志的在页⾯中的偏移量。
TRX_UNDO_DICT_TRANS:标记本组undo⽇志是不是由DDL语句产⽣的。
TRX_UNDO_TABLE_ID:如果TRX_UNDO_DICT_TRANS为真,那么本属性表⽰DDL语句操作的表的table id。
TRX_UNDO_NEXT_LOG:下⼀组的undo⽇志在页⾯中开始的偏移量。
TRX_UNDO_PREV_LOG:上⼀组的undo⽇志在页⾯中开始的偏移量。
TRX_UNDO_HISTORY_NODE:⼀个12字节的List Node结构,代表⼀个称之为History链表的节点

如果没有重用的情况下,如图
在这里插入图片描述
发生重用的条件,该链表中只包含⼀个Undo⻚⾯。
重用最后的效果:
insert undo链表重用:
以直接把旧的⼀组undo⽇志覆盖掉,写⼊⼀组新 的undo⽇志:
在这里插入图片描述

update undo链表重用:
在⼀个事务提交后,它的update undo链表中的undo⽇志也不能⽴即删除掉(这些⽇志⽤于MVCC,我们后边会说的)。所以如果之后的事务想重⽤update undo链表时,就不能覆盖之前事务写⼊ 的undo⽇志。这样就相当于在同⼀个Undo⻚⾯中写⼊了多组的undo⽇志,效果看起来就是这样:
在这里插入图片描述

回滚段

回滚段的概念

⼀个事务在执⾏过程中最多可以分配4个Undo⻚⾯链表,在同⼀时刻不同事务拥有的Undo⻚⾯链表是不⼀样的,所以在同⼀时刻系统⾥其实可以有许许多多个Undo⻚⾯链表存在。为了更好的管 理这些链表,又设计了⼀个称之为Rollback Segment Header的页⾯,在这个页⾯中存放了各个Undo⻚⾯链表的frist undo page的⻚号,他们把这些⻚号称之为undo slot。其实就是管理每条链表的头节点,也就能找到整条链表了。
在这里插入图片描述
回滚段其实这个段在mysql里面只有一个页面,和其他的段概念不太一样。
TRX_RSEG_MAX_SIZE:本Rollback Segment中管理的所有Undo⻚⾯链表中的Undo⻚⾯数量之和的最⼤值。换句话说,本Rollback Segment中所有Undo⻚⾯链表中的Undo⻚⾯数量之和不能超 过TRX_RSEG_MAX_SIZE代表的值。
TRX_RSEG_HISTORY_SIZE:History链表占⽤的页⾯数量。
TRX_RSEG_HISTORY:History链表的基节点。
TRX_RSEG_FSEG_HEADER:本Rollback Segment对应的10字节⼤⼩的Segment Header结构,通过它可以找到本段对应的INODE Entry
TRX_RSEG_UNDO_SLOTS:各个Undo⻚⾯链表的first undo page的⻚号集合,也就是undo slot集合。

从回滚段中申请Undo页⾯链表
每次从回滚段的第⼀个undo slot开始,看看该undo slot的值是不是FIL_NULL。不是的话就找下一个,如果是的话从段里申请一个页面作为first page,该undo slot的值设置为刚刚申请的这个页⾯的地址,这样也就意味着这个undo slot被分配给了这个事务

多个回滚段

⼀个事务执⾏过程中最多分配4个Undo⻚⾯链表,⽽⼀个回滚段⾥只有1024个undo slot,很显然undo slot的数量有点少啊。我们即使假设⼀个读写事务执⾏过程中只分配1个Undo⻚⾯链表, 那1024个undo slot也只能⽀持1024个读写事务同时执⾏,再多了就崩溃了。
如何解决undo slot不够?
所以设计InnoDB的⼤叔⼀⼜⽓定义了128个 回滚段,也就相当于有了128 × 1024 = 131072个undo slot。假设⼀个读写事务执⾏过程中只分配1个Undo⻚⾯链表,那么就可以同时⽀持131072个读写事务并发执⾏
回滚段存在哪里了?
是设计InnoDB的⼤叔在系统表空间 的第5号页⾯的某个区域包含了128个8字节⼤⼩的格⼦
在这里插入图片描述

回滚段的分类

我们把这128个回滚段给编⼀下号,最开始的回滚段称之为第0号回滚段,之后依次递增,最后⼀个回滚段就称之为第127号回滚段。这128个回滚段可以被分成两⼤类:
第0号、第33~127号回滚段属于⼀类。其中第0号回滚段必须在系统表空间中(就是说第0号回滚段对应的Rollback Segment Header页⾯必须在系统表空间中),第33~127号回滚段既可以在系统表 空间中,也可以在⾃⼰配置的undo表空间中,关于怎么配置我们稍后再说。 如果⼀个事务在执⾏过程中由于对普通表的记录做了改动需要分配Undo⻚⾯链表时,必须从这⼀类的段中分配相应的undo slot。
第1~32号回滚段属于⼀类。这些回滚段必须在临时表空间(对应着数据⽬录中的ibtmp1⽂件)中。 如果⼀个事务在执⾏过程中由于对临时表的记录做了改动需要分配Undo⻚⾯链表时,必须从这⼀类的段中分配相应的undo slot。 也就是说如果⼀个事务在执⾏过程中既对普通表的记录做了改动,又对临时表的记录做了改动,那么需要为这个记录分配2个回滚段,再分别到这两个回滚段中分配对应的undo slot。
总结⼀下针对普通表和临时表划分不同种类的回滚段的原因
在修改针对普通表的回滚段中的Undo页⾯时,需要记录对应的redo⽇志,⽽修改针对临时表的回滚段中的Undo页⾯时,不需要记 录对应的redo⽇志。

回滚段相关配置

默认情况下,针对普通表设⽴的回滚段(第0号以及第33~127号回滚段)都是被分配到系统表空间的
其中的第0号回滚段是⼀直在系统表空间的,但是第33~127号回滚段可以通过配置放到⾃定义的undo表空间中。但是这种配置只能在系统初始化(创建数据⽬录时)的时候使⽤
通过innodb_undo_directory指定undo表空间所在的⽬录,如果没有指定该参数,则默认undo表空间所在的⽬录就是数据⽬录。
通过innodb_undo_tablespaces定义undo表空间的数量。该参数的默认值为0,表明不创建任何undo表空间。 第33~127号回滚段可以平均分布到不同的undo表空间中。
⼩贴⼠: 如果我们在系统初始化的时候指定了创建了undo表空间,那么系统表空间中的第0号回滚段将处于不可⽤状态

设⽴undo表空间的⼀个好处就是在undo表空间中的⽂件⼤到⼀定程度时,可以⾃动的将该undo表空间截断(truncate)成⼀个⼩⽂件。⽽系统表空间的⼤⼩只能不断的增⼤,却不能截断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值