【MySQL学习笔记】深刻理解 redo 日志

1、什么是redo日志

redo日志,也被称为重作日志,对数据库中表的操做进行记录,能够用于系统崩溃时的数据恢复。web

例如,某个事务将系统表空间中的第100号页面中偏移量为1000处的那个字节的值1改成了2,咱们只须要记录将第0号表空间的100号⻚⾯的偏移量为1000处的值更新为2,这样咱们在事务提交时,把上述内容刷新到磁盘中,即便以后系统崩溃了,重启以后只要按照上述内容记录的步骤从新更新一下数据页,那么该事务对数据库中所作的修改又能够被恢复出来。数据库

2、redo日志的优势

与在事务提交时将全部修改过的内存中的页面刷新到磁盘中相比,只将事务执行过程当中产生的redo日志刷新到磁盘的好处以下:服务器

  • redo日志占用的空间很小
    存储表空间ID、页号、偏移量以及须要更新的值所须要的存储空间是很小的。
  • redo日志是顺序写入磁盘的
    在执行事务的过程当中,每执行一条语句,就可能产生若干条redo日志,这些日志是按照产生的顺序写入磁盘的,也就是使用顺序IO。
3、redo日志格式

在这里插入图片描述各个部分详细释义以下:svg

  • type:该条redo日志的类型
  • space ID:表空间
  • page number:页号
  • data:该条redo日志的具体内容
3.1 redo日志类型
  • MLOG_1BYTE(type字段对应的⼗进制数字为1):表示在⻚⾯的某个偏移量处写⼊1个字节的redo⽇志类型。
  • MLOG_2BYTE(type字段对应的⼗进制数字为2):表示在⻚⾯的某个偏移量处写⼊2个字节的redo⽇志类型。
  • MLOG_4BYTE(type字段对应的⼗进制数字为4):表示在⻚⾯的某个偏移量处写⼊4个字节的redo⽇志类型。
  • MLOG_8BYTE(type字段对应的⼗进制数字为8):表示在⻚⾯的某个偏移量处写⼊8个字节的redo⽇志类型。
  • MLOG_WRITE_STRING(type字段对应的⼗进制数字为30):表示在⻚⾯的某个偏移量处写⼊⼀串数据。

在这里插入图片描述

4、Mini-Transaction
4.1 以组的形式写入redo日志

一条语句在执行过程当中可能修改若干个页面。这些页面的更改都发生在Buffer Pool中,因此在修改完页面以后,须要记录下相应日志的redo日志。在执行语句的过程当中产生的redo日志在InnoDB中划分红了若干个不可分割的组,好比:spa

  • 更新Max Row ID属性时产生的redo日志是不可分割的。
  • 向聚簇索引对应的B+树的页面中插入一条记录时产生的redo日志是不可分割的。
  • 向某个二级索引对应的B+树的页面中插入一条记录时产生的redo日志是不可分割的。
  • 还有其余的一些对页面的访问操做时产生的redo日志也是不可分割的。

什么是不可分割?不可分割相似于事务,即要么都完成,要么都不完成。操作系统

咱们以向某个索引对应的B+树插入一条记录为例,在向B+树中插入这条记录以前,须要先定位到这条记录应该被插入到哪一个叶子节点表明的数据页中,定位到具体的数据页以后,有两种可能的状况:翻译

  • 状况一:数据页的剩余空间充足,足够容纳这一条待插入记录,这时,只要直接插入到这个数据页就行,记录一条类型为MLOG_COMP_REC_INSER的redo日志就行了,咱们把这种状况称之为乐观插入。
  • 状况二:该数据页剩余空间不足,这时须要进行页分裂操做,也就是新建一个叶子节点,而后把原先数据页中的一部分记录复制到这个新的数据页中,而后再把记录插入进去,再把这个叶子节点插入到叶子节点链表中,最后还要在内节点中添加一条目录项记录指向这个新建立的页面,很显然,这个过程要对多个页面进行修改,也就意味着会产生多条redo日志,咱们把这种状况称之为悲观插入。

若是在悲观插入过程当中,新的页面已经分配好了,数据也复制过去了,新的记录也插入到该页面中了,但是没有向内节点中插入一条目录项记录,这个插入就是不完整的,这样会造成一颗不正确的B+树。3d

InnoDB中规定,在执行这些保证原子性的操做时必须以组的形式来记录的redo日志,要么把所有的日志都恢复掉,要么一条都不恢复。如何作到这些的呢?日志

  • 有的须要保证原子性的操做会生成多条redo日志,好比向某个索引对应的B+树中进行一次悲观插入就须要生成许多条redo日志。如何把这些redo日志划分到一个组内的呢?就是在组中的最后一条redo日志后边加上一条特殊类型的redo日志,该类型名称为MLOG_MULTI_REC_END,type字段对应的十进制数字是31,此时至关于加了一个结束标志。这样在系统崩溃重启进行恢复时,只有当解析到类型为MLOG_MULTI_REC_END的redo日志,才认为解析到了一组完整的redo日志,才会进行恢复,不然的话,直接放弃前边解析到的redo日志。
    在这里插入图片描述xml

  • 有时候须要保证原子性的操做只生成一条redo日志,好比更新Max Row ID属性的操做就只会生成一条redo日志。其实在一条日志后边跟一个类型为MLOG_MULTI_REC_END的redo日志也是能够的,不过为了节省空间,他们不想浪费⼀个⽐特位。 别忘了虽然redo⽇ 志的类型⽐较多, 但撑死了也就是⼏⼗种, 是⼩于127这个数字的, 也就是说咱们⽤7个⽐特位就⾜以包括全部的redo⽇志类型,⽽type字段实际上是占⽤1个字节的, 也就是说咱们能够省出来⼀个⽐特位⽤来表示该须要保证原⼦性的操做只产⽣单⼀的⼀
    条redo⽇志, 示意图以下:
    在这里插入图片描述

4.2 Mini-Transaction的概念

顾名思义,Mini-Transaction就是微事务。把对底层页面中的一次原子访问的过程称之为一个Mini-Transaction,简称为mtr,一个所谓的mtr能够包含一组redo日志,在进行崩溃恢复时这一组redo日志做为一个不可分割的总体。
一个事务能够包含若干条语句,每一条语句实际上是由若干个mtr组成,每个mtr又能够包含若干条redo日志。

5、redo日志的写入过程
5.1 redo日志缓冲区

InnoDB为了解决磁盘速度过慢的问题而引入了Buffer Pool。同理,写入redo日志时也不能直接写到磁盘上,实际上在服务器启动时就向操做系统申请了一大片称之为redo log buffer的连续内存空间,翻译过来就是redo日志缓冲区,简称log buffer。

5.2 redo日志写入log buffer

向log buffer中写⼊redo⽇ 志的过程是顺序的, 也就是先往前边的block中写, 当该block的空闲空间⽤完以后再往下⼀个block中写。 当咱们想往log buffer中写⼊redo⽇ 志时, 第⼀个遇到的问题就是应该写在哪一个block的哪一个偏移量处, InnoDB提供了⼀个称之为buf_free的全局变量, 该变量指明后续写⼊的redo⽇ 志应该写⼊到log buffer中的哪一个位置。