Mysql 学习(十六)undo 日志

Undo日志

事务回滚需求

  • 我们都知道事务一旦失败,是可以将修改过的操作全部回滚的,但是这方式是怎么实现呢?
  • 每当我们要对一条记录做改动时(这里的改动可以指INSERT、DELETE、UPDATE),都需要留一手 —— 把回滚时所需的东西都给记下来。比方说:
    • 你插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。
    • 你删除了一条记录,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。
    • 你修改了一条记录,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。
  • 设计数据库的大佬把这些为了回滚而记录的这些东东称之为撤销日志,英文名为undo log,我们也可以土洋结合,称之为undo日志。

事务ID

  • 事务ID 本质上是一个数字
  • 分配策略:
    • 服务器会在内存中维护一个全局变量,每当需要为某个事务分配一个事务id时,就会把该变量的值当作事务id分配给该事务,并且把该变量自增1。
    • 每当这个变量的值为256的倍数时,就会将该变量的值刷新到系统表空间的页号为5的页面中一个称之为Max Trx ID的属性处,这个属性占用8个字节的存储空间。
    • 当系统下一次重新启动时,会将上面提到的Max Trx ID属性加载到内存中,将该值加上256之后赋值给我们前面提到的全局变量(因为在上次关机时该全局变量的值可能大于Max Trx ID属性值)。
  • 聚簇索引的记录除了会保存完整的用户数据以外,而且还会自动添加名为trx_id、roll_pointer的隐藏列,如果用户没有在表中定义主键以及UNIQUE键,还会自动添加一个名为row_id的隐藏列。
  • 给事务分配 事务ID 的时机:
    • 开启一个只读事务,当事务对临时表进行增,删,改操作的时候会分配事务ID
    • 开启一个读写事务,当事务第一次对某个表进行增,删,改操作的时候才会分配一个事务ID

Undo日志的格式

  • 为了实现事务的原子性,Innodb存储引擎在实际进行增,删,改一条记录时,都需要先把对应的undo日志记下来。这些记录会有序号,是从0开始,而这个序号被称为undo no
  • undo日志 是被记录在 页类型为 FIL_PAGE_UNDO_LOG 中的
  • 下面就来看看不同的操作会产生什么样的undo日志吧
  • 首先先创建一个undo_demo 表:
CREATE TABLE undo_demo (
    id INT NOT NULL,
    key1 VARCHAR(100),
    col VARCHAR(100),
    PRIMARY KEY (id),
    KEY idx_key1 (key1)
)Engine=InnoDB CHARSET=utf8;
  • 然后记住一个信息,这个表的table id 是 138

insert操作对应的undo日志

  • insert 操作可能会因为种种情况引发诸多修改,但是究其结果就是将这条记录放到一个数据页中,如果希望回滚这个插入操作,那么就把这个数据删除就好了
  • 下面就是插入对应的undo日志结构:在这里插入图片描述
    • 如果记录中的主键只包含一个列,那么在类型为TRX_UNDO_INSERT_REC的undo日志中只需要把该列占用的存储空间大小和真实值记录下来,如果记录中的主键包含多个列,那么每个列占用的存储空间大小和对应的真实值都需要记录下来(图中的len就代表列占用的存储空间大小,value就代表列的真实值)。
  • 下面我们模拟插入两条记录:
BEGIN;  # 显式开启一个事务,假设该事务的id为100

# 插入两条记录
INSERT INTO undo_demo(id, key1, col) 
    VALUES (1, 'AWM', '狙击枪'), (2, 'M416', '步枪');
  • 第一条undo日志的undo no为0,记录主键占用的存储空间长度为4,真实值为1
    在这里插入图片描述
  • 第二条undo日志的undo no为1,记录主键占用的存储空间长度为4,真实值为2。在这里插入图片描述
  • 这个时候还需要提到一个参数 roll_point ,这个参数就是每条记录指向 对应的undo日志的指针

DELETE操作对应的undo日志

写入 Undo 日志

通用链表结构

  • 在写入undo日志的时候会使用到很多链表,很多链表都有一些详细的结构,下面就是 List Node 结构示意图:
    在这里插入图片描述
  • Pre Node Page Number和Pre Node Offset的组合就是指向前一个节点的指针
  • Next Node Page Number和Next Node Offset的组合就是指向后一个节点的指针。
  • 为了更好的存储链表,还提出了一个基节点的结构,如图:在这里插入图片描述
  • List Length表明该链表一共有多少节点。
  • First Node Page Number和First Node Offset的组合就是指向链表头节点的指针。
  • Last Node Page Number和Last Node Offset的组合就是指向链表尾节点的指针。
  • List Base Node占用16个字节的存储空间。

FIL_PAGE_UNDO_LOG 页面

  • 之前讲表空间的时候,数据页是有不同类型的,所以存储 undo 日志的数据页是 FIL_PAGE_UNDO_LOG 类型,下图是这个数据页的结构:在这里插入图片描述

  • File HeaderFile Trailer是指页面头部和页面尾部,前面讲过就不提了,这里主要了解 Undo Page Header 结构:在这里插入图片描述

    • TRX_UNDO_PAGE_TYPE:存储什么种类的Undo日志
      • TRX_UNDO_INSERT(使用十进制1表示):类型为TRX_UNDO_INSERT_REC的undo日志属于此大类,一般由INSERT语句产生,或者在UPDATE语句中有更新主键的情况也会产生此类型的undo日志。
      • TRX_UNDO_UPDATE(使用十进制2表示),除了类型为TRX_UNDO_INSERT_REC的undo日志,其他类型的undo日志都属于这个大类,比如我们前面说的TRX_UNDO_DEL_MARK_REC、TRX_UNDO_UPD_EXIST_REC什么的,一般由DELETE、UPDATE语句产生的undo日志属于这个大类。
      • 一般一个页就只存储一个类型,不能混合存储
    • TRX_UNDO_PAGE_START:表示在当前页面中是从什么位置开始存储undo日志的,或者说表示第一条undo日志在本页面中的起始偏移量。
    • TRX_UNDO_PAGE_FREE:与上面的TRX_UNDO_PAGE_START对应,表示当前页面中存储的最后一条undo日志结束时的偏移量,或者说从这个位置开始,可以继续写入新的undo日志。
    • TRX_UNDO_PAGE_NODE:代表一个List Node结构

页面链表

单个事务页面链表

  • 在一个事务执行过程中,可能混合着 insert,update,delete 语句,也就是意味着会产生不同类型的 undo 日志,但是一个页面只存储一个大类的undo日志,所以一个事务的执行过程中,会产生两个页面链表,一个是 insert undo链表,一个是update undo链表在这里插入图片描述
  • 然后对普通表的改动和临时表的改动要分别记录,所以一个事务最多会有4条undo页面链表在这里插入图片描述
  • 然后什么时候分配链表呢,事务一开始就分配吗,当然不是,按需分配,什么时候需要什么时候再分配,不需要就不分配。
  • 就比如一下过程:
    • 刚刚开启事务时,一个Undo页面链表也不分配。
    • 当事务执行过程中向普通表中插入记录或者执行更新记录主键的操作之后,就会为其分配一个普通表的insert undo链表。
    • 当事务执行过程中删除或者更新了普通表中的记录之后,就会为其分配一个普通表的update undo链表。
    • 当事务执行过程中向临时表中插入记录或者执行更新记录主键的操作之后,就会为其分配一个临时表的insert undo链表。
    • 当事务执行过程中删除或者更新了临时表中的记录之后,就会为其分配一个临时表的update undo链表。

多个事务页面链表

  • 为了尽可能提高undo日志的写入效率,不同事务执行过程中产生的undo日志需要被写入到不同的Undo页面链表中。在这里插入图片描述

具体写入过程

  • 每个undo 链表都对应一个段,而这个段被称为 Undo Log Segment,这个链表中的数据页都是从这里面申请的,所以他们在Undo页面连表的第一页。
  • Undo 第一页存储的东西跟其他有所不同,最大的不同是这个Undo Log Segment Header 部分:在这里插入图片描述
    • 而 Undo Log Segment Header 的结构如下:在这里插入图片描述
      • TRX_UNDO_STATE:本Undo页面链表处在什么状态。
        • TRX_UNDO_ACTIVE:活跃状态,也就是一个活跃的事务正在往这个段里边写入undo日志。
        • TRX_UNDO_CACHED:被缓存的状态。处在该状态的Undo页面链表等待着之后被其他事务重用。
        • TRX_UNDO_TO_FREE:对于insert undo链表来说,如果在它对应的事务提交之后,该链表不能被重用,那么就会处于这种状态。
        • TRX_UNDO_TO_PURGE:对于update undo链表来说,如果在它对应的事务提交之后,该链表不能被重用,那么就会处于这种状态。
        • TRX_UNDO_PREPARED:包含处于PREPARE阶段的事务产生的undo日志。
      • TRX_UNDO_LAST_LOG:本Undo页面链表中最后一个Undo Log Header的位置。
      • TRX_UNDO_FSEG_HEADER:本Undo页面链表对应的段的Segment Header信息
      • TRX_UNDO_PAGE_LIST:Undo页面链表的基节点。
  • 一个事务向Undo页面写入Undo日志的方式,是直接基于尾部直接擦汗如,写完一个页面,就会从段里申请一个新的页面,然后把这个页面插入到Undo页面链表中。
  • 同一个事务向一个Undo链表写入的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日志中第一条undo日志的在页面中的偏移量。
      • TRX_UNDO_XID_EXISTS:本组undo日志是否包含XID信息。
      • 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日志在页面中开始的偏移量。

重用Undo页面

总结整个流程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值