原文链接:MySQL学习总结:提问式回顾 undo log 相关知识

1、redo 日志支持恢复重做,那么如果是回滚事务中的操作呢,也会有什么日志支持么?

  • 也回滚已有操作,那么就是想撤销,对应的有撤销日志,也叫做 undo log。
  • undo 日志分为两大类:「TRX_UNDO_INSERT」和「TRX_UNDO_UPDATE」,undo 日志需根据大类分开存储,不能混淆。

「TRX_UNDO_INSERT」对应的是insert语句、「TRX_UNDO_UPDATE」对应的是update语句和delete语句

  • 问题:那如何定位事务中对哪些记录做了改动?

    • 数据页记录中,有一个隐藏列「trx_id」,用于记录当前操作此记录的事务。
    • MySQL会在内存维护一个全局变量,专门为事务分配事务ID。
    • 每当需要为事务分配ID,则拿到上述全局变量,然后自增1。

    每当上述全局变量自增到256的倍数时,需要将此值刷新到磁盘中(系统空间表页号为5的页面的 Max Trx ID 属性中)。

  • 记录修改了,如何定位到对应的 undo log?
    • 在记录中有一个隐藏列「roll_point」,它会指向对应的 undo 日志。

2、undo 日志都是存在哪些地方,事务对表进行编辑,怎么分配的?

  • InnoDB支持128个回滚段,一个回滚段对应一个「Rollback Segment Header」页面。
  • 回滚段分为两大类:第0号、第33~127号属于一类,0号存放在系统表空间、其他的可以放在系统表空间或者自己配置 undo 表空间,这类用于存放普通表改动对应的 undo 日志;第1~32号属于一类,存放在临时表空间中,这类用于存放临时表改动对应 undo 日志。
  • InnoDB在系统表空间的5号页面的某个区域中,包含了128个8个字节大小的格子,用于保存128个「Rollback Segment Header」页面的地址。

    -「Rollback Segment Header」页面有一个重要部分是「TRX_RSEG_UNDO_SLOTS」:它表示各个 Undo 页面链表的 first undo page 的页号集合,也叫 undo slots 集合。

    • 因为一个页号占用4字节,而「TRX_RSEG_UNDO_SLOTS」一共是4096个字节,所以一共可以存储1024个lot。
    • 每个 slot 的初始值是 FIL_NULL,表示没有分配给其他事务使用。
  • 当事务需要分配 undo 页面链表时,
    • 先到回滚段对应的两个 cached 链表找是否有可用的 undo 页面;

    insert undo cache 链表和 update undo cache 链表。

    • 如果有则直接重用,否则需要回到「Rollback Segment Header」页中继续寻找;
    • 在「Rollback Segment Header」页中便利1024个 slot,看 slot 的值是否为 FIL_NULL,如果是的话,则申请一个 undo 页面作为该 undo 页面链表的 first undo page,接着将此页面的页号赋值给当前 slot。
    • 否则,表明已经有事务占用此 slot,需要继续往下寻找下一个 slot。
    • 如果到了最后一个回滚段的最后一个 slot,都没有找到可用的 slot,则给客户端返回异常:Too many active concurrent transactions。

大概如下图:

3、通过上面,我们都知道「roll_point」可以定位到对应的 undo 日志,但 undo 日志也是保存在磁盘中的,那又是怎么定位的呢?

  • 我们都知道聚簇索引以及二级索引,都是类型为「FIL_PAGE_INDEX」的页面;而 undo 日志也是存储在磁盘的,它对应的页面类型是「FIL_PAGE_UNDO_LOG」。
  • roll_point 组成部分:
    • is_insert:是否是「TRX_UNDO_INSERT」大类的 undo 日志
    • rseg id:回滚段编号
    • page number:指针,指向 undo 日志所在页面的页号。
    • offset:指针,指向 undo 日志在页面中的偏移量。
  • 所以,我们可以根据「roll_point」中的属性,找到对应的回滚段、接着找到对应的页面,最好定位到页面中的偏移量。

4、如果一个页面无法存储当前事务生成的日志,要多个页面才能完成,undo 日志间如何联系?

  • undo 日志页面有一个特有的部分:「Undo Page Header」。

数据页都有一个共有的部分:「File Header」,页面间可利用链表完成联系,就是靠「File Header」 中的「FIL_PAGE_PREV」和「FIL_PAGE_NEXT」属性。

但这是页面之间的链表,如果要做到 undo 日志的链表,还需更细的连接信息。

  • TRX_UNDO_PAGE_TYPE:上面提到的 und 日志分为两个大类:「TRX_UNDO_INSERT」和「TRX_UNDO_UPDATE」
  • TRX_UNDO_PAGE_START:当前页面存储第一条 undo 日志的开始偏移量
  • TRX_UNDO_PAGE_FREE:当前页面存储最后一条 undo 日志的结束偏移量
  • TRX_UNDO_PAGE_NODE:
    • Prev Node Page Number:前一个节点的页号
    • Prev Node Offset:前一个节点页内的偏移量
    • Next Node Page Number:后一个节点的页号
    • Next Node Offset:后一个节点页内的偏移量
  • 所以,可以利用每个 undo 日志页「Undo Page Header」的「TRX_UNDO_PAGE_NODE」属性来组成一个链表。

5、undo 日志也支持重用么?如果支持,如何覆盖 undo 日志?

  • 重用条件:

    • 首要条件:undo 页面链表对应的事务已经提交
    • 第二:undo 页面链表只包含一个 undo 页面
    • 第三:该页面已使用的空间小于整个页面空间的3/4
  • 重用策略:
    • insert undo 链表:因为对于新增记录,只要事务提交了,对应的 undo 日志就没啥用了,所以可以直接覆盖。
    • update undo 链表:对于更新/删除记录,即使提交了,也不能立马删除对应的 undo 日志,因为 MVCC 需要利用此链表做文章;所以只能在后面接着写入 undo 日志,即一个 undo 页面,写入多组 undo 日志。

6、如果 undo 日志支持重用,那怎么知道从哪里开始写入第二组 undo 日志?

  • undo 日志的页面有一个非常重要的部分:Undo Log Header。
  • 它包含:
    • TRX_UNDO_TRX_ID:本组 undo 日志对应的事务id
    • TRX_UNDO_TRX_NO:事务提顺序,事务提交后会生成一个序号;先提交的序号小。
    • TRX_UNDO_LOG_START:本组 undo 日志 中第一条 undo 日志在页面中的偏移量
    • TRX_UNDO_NEXT_LOG:下一组 undo 日志在页面中开始的偏移量(支持 undo 日志页面复用)
    • TRX_UNDO_PREV_LOG:上一组 undo 日志在页面中开始的偏移量(支持 undo 日志页面复用)
    • ......
  • 因此,只需拿到「TRX_UNDO_NEXT_LOG」对应的偏移量,即可知道在哪里开始继续写入第二组 undo 日志。

7、insert语句和 undo 日志

  • 插入一条类型为「TRX_UNDO_INSERT_REC」 的 undo 日志,用于支持回滚操作。
  • undo 日志里最主要是记录了插入的记录的主键信息:<len,value>列表
    • len 为主键类型所占存储空间的长度,例如主键类型为int,占用4字节,那么len为4
    • value 为主键的真实值,例如 id 列为主键,插入记录的主键 id=2,那么value为2

8、delete语句和 undo 日志

  • 删除阶段:

    • 插入一条类型为「TRX_UNDO_DEL_MARK_REC」的 undo 日志,用于支持回滚操作。

      • 将该记录的 trx_id 和 roll_point 旧值记录到 undo 日志对应的属性中。
    • delete_mark 阶段:将记录的「delete_flag」置为1,表示已经删除
      • 还是保留在正常记录链表中,保留这个中间状态是为了支持MVCC
      • 还会修改 trx_id、roll_point等隐藏列
    • purge阶段:当事务提交时,会有专门的线程真正删除此记录
      • 这里的真正删除指的是,将记录从正常记录链表中移除,加入到垃圾记录链表的表头。

        • 将记录的 next_record 指向 Page Header 中的 PAGE_FREE 属性
        • 将 Page Header 的 PAGE_FREE 属性执行此记录
      • 修改 Page Header 中的 PAGE_GARBAGE,表示页面中可重用的字节数量
      • purge 阶段不需要 undo 日志,因为此阶段在事务提交后执行。
  • 扩展点:

    • 每当新插入一条记录,都会先判断垃圾链表的头节点代表的已删除记录的存储空间是否满足新纪录,如果满足,直接复用。

      • 疑问:如果新插入的记录一直都无法使用垃圾链表的头节点对应的存储空间,岂不是一直会存在碎片空间?下面第三点会解释!
    • 如果无法满足,则需要直接向页面申请新的空间来存储。
    • 但如果此时页面没有足够的空间来存储新纪录,那么就会判断「PAGE_GARBAGE」中的碎片空间和剩余的可用空间加起来是否能满足,如果可以,那么就会进行页面的重新组织
      • 开辟一个临时页面,把本页面的记录依次插入
      • 接着将临时页面复制到本页面,接着释放碎片空间。
      • 疑问:如果「PAGE_GARBAGE」中的碎片空间和剩余的可用空间加起来都不能满足呢?是不是会有页面分裂?
    • 否则,如果页面的碎片空间和剩余空间都不足以存放新纪录,那么只能进行页面分裂。

9、update语句和 undo 日志

  • update 分两种情况:更新主键和不更新主键
  • 不更新主键:也分为两种情况,一种是更新前后所占存储空间大小不变;另外一种是,更新前后所占存储空间变大或变小。
    • 就地更新:

      • 在原有记录更新
      • 插入一条类型为「TRX_UNDO_UPD_EXIST_REC」的 undo 日志,用于支持回滚操作。
        • 包含主键各列信息(<len,value>列表)、索引列各列信息(<pos,len,value>列表)、被更新的列更新前信息。
    • 先删除再插入:
      • 用户线程同步删除旧记录,更新相关的页面统计信息。
      • 在当前页申请新的空间插入一条变更后的记录,如果页面剩余的存储空间不足,则需要进行页面分裂。
      • 插入一条类型为「TRX_UNDO_UPD_EXIST_REC」的 undo 日志
  • 更新主键:
    • 对旧id记录执行删除操作,注意:这里只是执行 delete_mark 阶段,避免其他事务无法访问此记录。

    插入一条类型为「TRX_UNDO_DEL_MARK_REC」的 undo 日志

    • 接着根据更新后的个列值创建一条新纪录,并插入到聚簇索引中。

    插入一条类型为「TRX_UNDO_INSERT_REC」 的 undo 日志

MySQL学习总结:提问式回顾 undo log 相关知识的更多相关文章

  1. 提问式复习:图文回顾 redo log 相关知识

    原文链接:提问式复习:图文回顾 redo log 相关知识 1.如何提升 redo日志 的写性能? 为了保证 redo日志 不丢失,会在磁盘中开辟一块空间将日志保存起来.但是这样会有一个问题,磁盘的读 ...

  2. OpenCV&Qt学习之四——OpenCV 实现人脸检测与相关知识整理

    开发配置 OpenCV的例程中已经带有了人脸检测的例程,位置在:OpenCV\samples\facedetect.cpp文件,OpenCV的安装与这个例子的测试可以参考我之前的博文Linux 下编译 ...

  3. 5.14日学习内容1:jquery表单相关知识

    <script> $comment.animate({height:'+=50'},400);//在原来的基础上加50: $('.smaller').click(function(){ i ...

  4. 学习一下 JVM (二) -- 学习一下 JVM 中对象、String 相关知识

    一.JDK 8 版本下 JVM 对象的分配.布局.访问(简单了解下) 1.对象的创建过程 (1)前言 Java 是一门面向对象的编程语言,程序运行过程中在任意时刻都可能有对象被创建.开发中常用 new ...

  5. 详细分析MySQL事务日志(redo log和undo log)

    innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...

  6. 详细分析MySQL事务日志(redo log和undo log) 表明了为何mysql不会丢数据

    innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...

  7. MySQL必知必会:简介undo log、truncate、以及undo log如何帮你回滚事物

    目录 一.前言 二.undo log表空间 三.关于undo log默认的配置 四.如何将undo log放到单独的表空间 文章公众号首发,持续更新中 五.rollback segment 六.什么是 ...

  8. 详细分析MySQL事务日志(undo log)

    2.undo log 2.1 基本概念 undo log有两个作用:提供回滚和多个行版本控制(MVCC). 在数据修改的时候,不仅记录了redo,还记录了相对应的undo,如果因为某些原因导致事务失败 ...

  9. 深入学习MySQL 02 日志系统:bin log,redo log,undo log

    上一篇文章中,我们了解了一条查询语句的执行过程,按理说这篇应该讲一条更新语句的执行过程,但这个过程比较复杂,涉及到了好几个日志与事物,所以先梳理一下3个重要的日志,bin log(归档日志).redo ...

随机推荐

  1. OJ 注意事项

    1,检查指针是否有效,即是否为NULL 1 void OutputMaxAndMin(int * pInputInteger, int InputNum, int * pMaxValue, int * ...

  2. 关于Mybatis中表中字段名和POJO中字段名不同的解决方法

    项目结构: POJO中: package com.domain; /** * @author mzy * 定义orders表对应的实体类 */ public class Order { /** * C ...

  3. RabbitMq死信队列(接盘侠)

    队列创建之后,后期对其修改或者参数添加会报错.需要把队列重新删除,重新创建线上环境不能把队列删除,优雅安全的方式是重新建一个队列,把死信队列相关的队列进行绑定 在有过期时间的队列中设定最大接收能力5条 ...

  4. mysql8.0----mysqldump抛出:Unknown table 'COLUMN_STATISTICS' in information_schema (1109)

    问题:我尝试使用mysqldump时,得到以下错误: 复制 $> mysqldump --single-transaction --h  -u user -p db > db.sql my ...

  5. Servlet学习笔记(一)之Servlet原理、初始化、生命周期、结构体系

    Servlet是用java语言编写的应用到Web服务器端的扩展技术,与java对象的区别是,Servlet对象主要封装了对HTTP请求的处理,并且它的运行需要Servlet容器的支持(以下会介绍原因, ...

  6. 尚硅谷 Go语言核心编程资料

    链接:https://pan.baidu.com/s/1zn8Jf82lxg-2msVS1Iedeg  提取码:5vsg  复制这段内容后打开百度网盘手机App,操作更方便哦

  7. 原子操作cas

    一.概念, 基于处理器指令,把比较和交换合成一个指令完成,保证了原子性: 因为是针对一个内存地址值的,一个内存地址指向一个变量,所以只对一个共享变量能保证原子性: 二.原子操作类 锁只有synchro ...

  8. MySQL实战45讲(01--05)-笔记

    目录 MySQL复习 01 | 基础架构:一条SQL查询语句是如何执行的? 连接器 查询缓存 分析器 优化器 执行器 02 | 日志系统:一条SQL更新语句是如何执行的? 重要的日志模块:redo l ...

  9. RabbitMQ核心知识总结!

    本文已经收录到github仓库,此仓库用于分享Java相关知识总结,包括Java基础.MySQL.Spring Boot.MyBatis.Redis.RabbitMQ.计算机网络.数据结构与算法等等, ...

  10. MySql分表、分库、分片和分区的区别

    一.前言 数据库的数据量达到一定程度之后,为避免带来系统性能上的瓶颈.需要进行数据的处理,采用的手段是分区.分片.分库.分表. 二.分片(类似分库) 分片是把数据库横向扩展(Scale Out)到多个 ...