浅谈 MySQL 的 Undo log 日记

打印 上一主题 下一主题

主题 1115|帖子 1115|积分 3345

undo log 存储在类型为 FIL_PAGE_UNDO_LOG 页中。 可以从系统表空间中分配空间,也可以从 undo tablespace 中分配空间。 FIL_PAGE_UNDO_LOG 类型页主要分为四部分:


  • File Header,所有页都有的结构
  • Undo Page Header

    • TRX_UNDO_PAGE_TYPE,存储什么类型的 undo log,分类背面会先容。 不同类型的 undo log 不能存储在同一个页中。 TRX_UNDO_INSERT_REC 类型的放一个页面中,其他类型的放一个页面中。
    • TRX_UNDO_PAGE_START,本页中 undo log 开始的偏移量。
    • TRX_UNDO_PAGE_FREE,本页中,空闲位置开始的偏移量,与最后一条 undo log 的末端偏移量一样。
    • TRX_UNDO_PAGE_NODE,12 字节,包罗4部分内容

      • Pre Node Page Number 和 Pre Node Offset 组成了指向前一个 FIL_PAGE_UNDO_LOG 页的指针;
      • Next Node Page Number 和 Next Node Offset 组成了指向后一个 FIL_PAGE_UNDO_LOG 页的指针;


  • 用于存放 undo log 日记记载的空间
  • File Trailer,所有页都有的结构
01-INSERT 对应的 undo log 记载格式

插入区分乐观插入、悲观插入,


  • 乐观插入,指要插入页的空间充足,足以容纳新插入的记载。
  • 悲观插入,指要插入页的空间不敷,必要页分裂。
不管怎样,是将一条记载插入。 它对应的回滚利用,就是删除这条插入的记载,TRX_UNDO_INSERT_REC:


  • end of record
  • undo type,TRX_UNDO_INSERT_REC,说明为 INSERT 语句的 undo log。
  • undo no,序列号。
  • table id,可以从 information_schema.INNODB_TABLES 中查询到。
  • 主键各列信息,{<len, value>}。INSERT 相对应的回滚日记是删除插入的记载,所以这里要记载的信息必要包含怎样定位到这条记载。
  • start of record
此中,start of record 和 end of record 是两个指针,指向了 undo log 记载开始、结束的位置。
02-DELETE 对应的 undo log 记载格式

在先容 记载 和 页 时有提到,数据页中所有的记载通过 next_record 串联起来,形成一个链表。 所有的被标记为删除(即 delete_mask 为1)的记载也会通过 next_record 串联起来,形成垃圾链表。 数据页的 Page Header 中有一个 PAGE_FREE 属性,指向垃圾链表的头部。
首先,我们要先搞明白 DELETE 语句的实行过程。删除分为两个阶段:

  • 在聚簇索引上根据 id 找到对应的记载,将其 delete_mask 置为1(同时也会修改隐蔽列 DB_TRX_ID 和 DB_ROLL_PTR)。此阶段也被称为 delete mark 阶段。
  • 当实行删除语句的事务提交后,有专门的线程()将 delete_mask 标记为1的记载从正常记载中移除,加入到垃圾链表中(头插)。 同时必要修改一些页面的其他信息,比如页面中的用户记载数目 PAGE_N_RECS、上次插入记载的位置 PAGE_LAST_INSERT、垃圾链表头节点的指针 PAGE_FREE、页面中可重用的字节数目 PAGE_GARBAGE、还有页目次的一些信息等等。 此阶段也被称之为 purge 阶段。
delete mask 阶段对应的 undo log 类型为 TRX_UNDO_DEL_MARK_REC:


  • end of record
  • undo type,TRX_UNDO_DEL_MARK_REC,说明为 delete mask 阶段的 undo log。
  • undo no,序列号。
  • table id
  • info bits
  • old trx_id
  • old roll_pointer,和 old trx_id 一块,记载了修改 delete_mask 前,记载上的值。
  • 主键各列信息,{<len, value>},主要为了定位记载。
  • index_col_info len,记载了当前、以及下部分内容占用的空间。
  • {<pos, len, value>},生存了记载中的列(在某个索引中),其在索引中的位置、空间占用、实际值。 这部分内容主要在 purge 阶段使用。
  • start of record
03-UPDATE 对应的 undo log 记载格式

UPDATE 语句的处理方式根据是否更新主键分为两种不同的方案:

  • 不更新主键。 根据是否改变记载的大小,又可以进一步分为:

    • 就地更新,记载大小不发生厘革。 对记载中的每个列来说,其大小都不发生改变。
    • 先删除,再插入。 对记载中的每一列来说,任一列的大小发生了厘革。 实行的利用是,先删除、再插入。 删除是彻底删除,并不是 DELETE 中分两阶段的删除。 与 purge 阶段不同的事,并非是某个特定线程去删除,而是由用户线程同步删除。
    针对不更新主键的情况,undo log 类型为 TRX_UNDO_UPD_EXIST_REC(省略了 end of record 之类的共有的信息):
       

    • old trx_id
    • old roll_pointer
    • 主键各列信息,{<len, value>},主要为了定位记载。
    • n_updated,记载了共有多个列被更新了。
    • {<pos, old_len, old_value>} 记载了被更新的列的旧值
    • index_col_info len,记载了当前、以及下部分内容占用的空间。
    • {<pos, len, value>},生存了记载中的列(在某个索引中),其在索引中的位置、空间占用、实际值。

  • 更新主键。分为两步处理:

    • 对旧记载举行 delete_mask 阶段的利用。
    • 插入新的记载。
    所以,这种情况会产生两条 undo log 记载。

04-版本链

在学习 InnoDB 行记载格式时,我们知道在聚簇索引中记载的数据部分 InnoDB 会自动插入三列:DB_ROW_ID\DB_TRX_ID\DB_ROLL_PTR。 此中 DB_ROW_ID 不是必须的,只在没有指定主键且没有唯一列时插入。 其余两列与事务及一致性读视图相关:


  • DB_TRX_ID,每次一个事务对某条聚簇索引记载改动时,都会将事务的 ID 赋给隐蔽的 DB_TRX_ID 列
  • DB_ROLL_PTR,每次一个事务对某条聚簇索引记载改动时,旧版本会被写入到 undo log 中,该隐蔽列就像一个指针,通过它来找到之前的版本。
对某条记载的每次修改(UPDATE、DELETE),都会在 undo log 中记载一个旧版本,旧版本中也包罗 DB_ROLL_PTR 列、事务 ID 列。 因此,undo log 中记载的所有版本会形成一个链表,值越旧,它就在链中越靠近末端的位置,该记载的最新值,记载在聚簇索引中的记载上。
我们借用《MySQL 是怎样运行的:从根儿上理解 MySQL》中的一幅图,加深下对版本链的理解。

图中所示的是插入、并删除一条记载后,形成的版本链。
在读未提交事务隔离级别下,直接读取记载的最新版本即可; 在串行级别下,必要使用锁来包管。 所以,版本链主要应用于读提交和可重复读事务隔离级别。 它要解决的焦点问题就是,版本链中的哪些版本是当前事务可见的。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

笑看天下无敌手

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表