MySQL(七):undo日志——保证事务的原子性

一、前言

1.1 如何回滚事务

原子性是事务的其中一个特性,指的是要么全部执行完,要么全都不执行,说的容易,如何保证这一特性呢?

事务在执行的过程中难免会发生某些意外:

  • 服务器错误、操作系统错误、电脑直接掉电、、、
  • 有时程序员也想手动的回滚事务

上述这些情况,都会导致事务执行一般就结束,但是事务在执行过程中已经修改了很多数据,为了保证事务的原子性,我们必须改回原来的样子。


数据库的回滚操作和悔棋类似,如果向数据库中插入了一条数据,对应的回滚操作就是把这条记录删除掉,更新了一条记录,对应的回滚操作就是把该记录更新回旧值;
因此,每当对一条记录进行改动的时候,都需要将回滚所需的东西记录下来,

  • 插入一条记录时,要记录该记录的主键,便于回滚时根据主键删除记录
  • 删除一条记录时,要把该记录的内容记下来,

MySQL中规定,把用来记录事务回滚时所需信息的日志称为undo日志,由于select不会对记录进行改动,所以进行select操作时,不用记录undo日志.

1.2 事务id

如果某个事务在执行过程中对某个表执行了增删改操作,那么InnoDB存储引擎会为该事务分配一个独一无二的事务id,对于只读事务来说,只有在它对用户创建的临时表进行增删改查操作时,才会为该事务分配一个事务id,否则不会分配;

1.3 roll pointer 隐藏列

roll pointer作为用户记录的隐藏列,本质上是一个指向记录对应的undo日志的指针
在这里插入图片描述

1.4 trx_id 隐藏列

trx_id保存的是,对该条聚簇索引记录进行改动的语句所在的事务id

二、undo日志

2.1 undo日志的格式

在某些更新操作中,一条更新语句,可能对应多条undo日志,这些日志从0编号,被放在类型为FIL_PAGE_UNDO_LOG的页面中。

2.2 insert 对应的undo日志

在这里插入图片描述

  • undo no:在一个事务中,undo no 从0开始递增,每生成一条undo日志,undo no增加1
  • 主键个列信息:如果主键中包含多个列,会将这多个列占用的存储空间大小和对应的真实值记录下来

2.3 delete 操作对应的undo日志

向页面中插入的记录会根据记录头信息中的next_record属性组成一个单向链表,同样,被删除的记录也会根据记录的头信息中的next_record组成一个链表,只不过在该链表中的记录占用的空间可以被覆盖,所以也称为垃圾链表

通过delete语句删除一条记录分为以下两个阶段:

  1. 仅将记录的deleted_flag置为1,该阶段并不会将该记录加入到垃圾链表中,算是一条处于中间状态的记录
    在这里插入图片描述
  2. 当该删除语句所在的事务提交之后,会将该记录头插到垃圾链表中,因此也会更改PAGR_FREE属性
    在这里插入图片描述
    因此delete语句对应的undo日志,只需考虑对阶段1所做的影响进行回滚就可以了。
    在这里插入图片描述
    注意:
    在对一条记录进行delete mark操作前,需要把该记录的trx_id和roll_pointer隐藏列的旧值写到对应的undo日志中,这样有一个好处就是,可以通过undo日志的roll_pointer属性找到上一次对该记录进行改动时产生的undo日志
    在这里插入图片描述

2.4 update操作对应的undo日志

在执行update语句时,InnoDB在对更新主键和不更新主键采取两种不同的方案

不更新主键的情况中也可分为被更新列占用的存储空间是否发生变化两种情况

(1)当被更新列占用的存储空间不发生变化时,采用就地更新的方式

(2)当被更新列占用的存储空间发生变化时,会先删除旧纪录,再插入新纪录,这里所说的删除不是delete mark,而是真正的删除


更新主键的情况

在聚簇索引中,记录之间根据主键进行排序,如果我们将索引从1更新成10000,并且在1-10000之间还有很多记录,此时主键值为1的记录和主键值为10000的记录之间会离的很远,对于这中情况,InnlDB分两步处理:
在这里插入图片描述

第一步:对旧纪录进行delete mark操作,记录一条类型为 TRX_UNDO_DEL_MARK_REC的undo 日志
第二步:插入一条新的记录到聚簇索引中,记录一条类型为TRX_UNDO_INSERT_REC的undo日志
也就是说,对更新一条记录的主键值时,会生成两条undo日志

2.5 Undo页面链表

在一个事务中,可能包含多个语句 ,所以在一个事务中可能产生很多的undo日志,这些日志可能一个页面中放不下,需要放到多个页面中,多个页面形成一个链表
在这里插入图片描述
InnoDB中规定,同一个undo页面中,要么只存储TRX_UNDO_INSERT大类的undo日志,要么只存储TRX_UNDO_UPDATE大类的undo日志,不能混着存,所以一个事务在执行过程中,需要2个undo页面的链表

在这里插入图片描述

2.6 undo日志写入过程

InnoDB中规定,每个Undo页面链表都对应着一个段,称为Undo Log Segment,链表中的页面都是从这个段中申请的,
在这里插入图片描述
Undo页面链表的第一个页面比普通页面多了一个Undo Log Segment Header
在这里插入图片描述
TRX_UNDO_STATE:当前页面链表处于什么状态
TRX_UNDO_LOG:Undo页面链表中最后一个Undo Log Header 的位置
TRX_UNDO_FSEG_HEADER:本Undo 页面链表对应的段的Segment Header信息
TRX_UNDO_PAGE_LIST:Undo页面链表的基节点

2.6.1 Undo Log Header

一个事务在向Undo页面中写入undo日志时,采用的方式很简单,直接往里"堆",写完一个Undo页面后,再从段中申请一个新页面
InnoDB中规定,同一事务向一个Undo页面链表中写入的undo日志算是一组

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日志在页面中的偏移量

TRX_UNDO_NEXT_LOG:下一组undo日志在页面中开始的偏移量
TRX_UNDO_PREV_LOG:上一组undo日志在页面中开始的偏移量

2.7 回滚段

2.7.1 回滚段的概念

一个事务在执行的过程中最多可以分配4个Undo页面链表,因此同一时刻可以存在多个Undo页面链表,为了更好的管理这些Undo页面链表,InnoDB设计了一个名为Rollback Segment Header的页面,在这个页面中存放了各个Undo页面链表的first undo page的页号,这些页号称为undo slot

在这里插入图片描述
每一个Rollback Segment Header属于一个Rollback Segmeng段,同时,这个回滚段中其实只有一个页面

TRX_RSEG_MAX_SIZE:这个回滚段中所有undo链表中的所有undo页面之和的最大值
TRX_RSEG_HISTORY_SIZE:History链表占用的页面数量

TRX_RSEG_HISTORY:History链表的基节点
TRX_RSEG_FSEG_HEADER:这个回滚段对应的10字节大小的Segment Header结构
TRX_RSEG_UNDO_SLOTS:各个undo页面链表的first undo page的页号集合

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值