mysql——redo日志

本文详细介绍了MySQL的Redo日志,包括其作用、存储结构、日志类型、写入流程、日志刷盘策略以及与事务提交的关系。Redo日志用于确保事务的持久性和系统崩溃后的恢复,通过记录对数据页的修改,即使在内存中的数据丢失,也能根据Redo日志恢复。此外,文章还讨论了日志的管理方式,如日志组、MTR(Mini-Transaction)以及LSN(Log Sequence Number)等概念。
摘要由CSDN通过智能技术生成

概念

        如果我们只在内存的Buffer Pool中修改了页面,假设在事务提交后突然发生了某个故障,导致内存中的数据都失效了,那么这个已经提交的事务在数据库中所做的更改也就丢失了。针对这种问题,mysql提供了redo日志。

        redo日志记录的是某个表空间某个页的第几个偏移量变成了什么值,能够解决:

1.每进行一个操作就进行一次刷盘动作导致的浪费

2.顺序io,能够提升性能,降低在io上的消耗

redo简单日志类型

  • MLOG_1BYTE(type=1)在某个表空间的某一页的某个偏移量写一个1字节的redo日志,数据占用的字节数为1字节
  • MLOG_2BYTE(type=2)数据占用的字节数为2字节
  • MLOG_4BYTE(type=4)数据占用的字节数为4字节
  • MLOG_8BYTE(type=8)数据占用的字节数为8字节

上述的四种类型是简单日志类型中常用的数据类型,所以单独提取出来作为基础结构。

  • MLOG_WRITE_STRING(type=30)对应下图中第三种类型,此时数据所占的字节数不固定,比较灵活,如果为8时则与 MLOG_8BYTE 一致
简单日志存储结构

第一种类型是通用结构,第二种是1字节,2字节,4字节以及8字节对应的结构,第三种是MLOG_WRITE_STRING对应的结构。

类型即为对应的type,表空间id+页号能定位到对应的页,页面中的偏移量即为对页面中的哪个数据进行操作,redolog的具体内容即为对数据的具体操作。

简单日志类型存储的内容:对数据的修改只有简单操作

使用场景:

在记录的头信息中,在记录的真实数据中有一个隐藏字段叫row_id,当同时满足以下两种条件:1.未指定主键、2.没有非空且unique的列;表中就无法唯一标识某一条记录,此时mysql会在记录中默认生成一个row_id,生成规则为:在内存中,有一个全局变量,需要插入一条记录且满足上述两个条件时,读取该全局变量,赋值给row_id;全局变量需要+1;数据一直插入,row_id一直递增,当增加至256的倍数时,会将该数放到7号表空间的MaxRowId属性中;在row_id一直递增过程中,如果mysql突然宕机,在启动的时候,会去读取MaxRowId(一定是256的倍数),因为考虑到数据丢失的情况,所以会将读取到的值再+256,作为最新的rwo_id,此时可能会产生不连续的情况;

当数据提交的时候,此时数据并没有存在磁盘中,只是保存在bufferpool中,但是一定要保证数据存储在redolog中,否则一旦宕机,就无法恢复数据,此时存储的就是简单数据类型,只是对MaxRowId进行简单赋值。MaxRowId定义的是8个字节,直接使用MLOG_8BYTE类型。

redo复杂日志类型

  • MLOG_REC_INSERT(type=9)非紧凑型插入
  • MLOG_COMP_REC_INSERT(type=38) 紧凑型插入(现在使用的都是compact紧凑型)
  • MLOG_COMP_PAGE_CREATE(type=58) 创建页的类型
  • MLOG_COMP_REC_DELETE(type=42)  删除的类型
  • MLOG_COMP_LIST_START_DELETE(type=44) 删除掉页面上一些紧凑的记录,起始于start
  • MLOG_COMP_LIST_END_DELETE(type=43)删除掉页面上一些紧凑的记录,起始于end
  • MLOG_ZIP_PAGE_COMPRESS(type=51)压缩
  • 。。。

这些类型对应的就是对数据库中不同类型的操作。

复杂日志存储结构​​​​​

如上图:

        类型就是每种复杂类型对应的type;表空间+页号能够定位到某个表的某页记录;

        这里的每个字段都有其意义,在redolog底层其实是使用函数来将存储的log拼接成对应的增删改语句,所以这些数据都是作为函数的入参存在的

执行insert语句的记录过程

 上图是执行一条insert语句可能引发的变化。

 redo日志组

        为了防止系统宕机后在恢复的过程中,因为只读取到部分redolog导致恢复不完全,所以要求redolog有不可分割,原子性;一条语句可能会产生很多个redolog,所以需要对其进行管理,将一条语句产生的所有redoloog放在一个组里,这样能保证其不可分割、原子性。

主要包括:

1.简单日志类型的MaxRowId变化

2.一条语句引发的聚簇索引发生的所有变化(聚簇索引变化结束后)

3.二级索引发生的所有变化

 使用特殊的redolog表示在其之前的redolog都是一个组中,如果顺序读取,没有读取到结尾标识类型的redolog,那么认为保存redolog时出现了问题,那么当前的组,不进行恢复。

此处需要注意的是,上述几种情况下,如果是简单日志类型,记录时只会有一个redolog,如果再添加一个end,会浪费,所以为了节省空间,在type字段中做了特殊处理,如下图所示:

type特殊处理

 因为目前类型只使用了50个左右,所以八个字节取出一个字节用来标记是否是单一的redolog,剩余七个字节用来代表redo日志的类型。

 MTR(Mini - Transaction)

对底层页面进行一次原子访问的过程被称为一个Mini-Transaction。

redolog都是对底层页面进行一个原子访问

由上图可知:

  •  1个事务可以包含n条SQL语句
  •  1条SQL语句可以包含n个MTR
  •  1条MTR可以包含n条redo日志

redo log block

redolog底层都是使用block的形式存储的,其大小固定为512字节,结构如下图所示:

 由上图可知,主要分成三个部分:

  • log block header:占用12字节
    • LOG_BLOCK_HDR_NO:4字节,每一个block都有对应的编号
    • LOG_BLOCK_HDR_DATA_LEN:2字节,用来统计占用大小,当body为空时,统计header的大小12字节,body中有数据,再加上body的大小,但当body存满496字节的时候,此时大小为512字节
    • LOG_BLOCK_FIRST_REC_GROUP:2字节,存储第一个redolog日志组的偏移量,因为redolog是紧密排列的,所以找到第一个之后,就能顺序找到后续的组
    • LOG_BLOCK_CHECKPOINT_NO:4字节,checkpoint的编号
  • log block body:存储redolog的日志内容
  • log block tailer:
    • LOG_BLOCK_CHECKSUM:只是用作校验

redo日志缓冲区— — log buffer

 为了减少对磁盘的io操作,对redo的操作尽量都放到内存中,所以跟page类似,都设置有buffer。

 redo日志写入log buffer

log buffer和bufferpool一样都是向os申请的一块连续的空间。、

为了保证顺序io,所以写入log buffer一定是顺序写入。

因为事务的执行可以是并发的,所以写入log buffer的时候,也是可以交替写入的,比如事务T1有两个MTR:mtr_t1_1 和 mtr_t1_2
事务T2的两个MTR:mtr_t2_1 和 mtr_t2_2,那么在并发的情况下,写入可能会出现下图所示的情况:

 此处需要注意的是:因为mtr_t1_2的数据比较大,所以需要跨三个block存储,此时最后一个block的LOG_BLOCK_FIRST_REC_GROUP写入的是mtr_t1_2的end位置;另外,buf_free是一个全局变量,保存了当前的偏移量,所以写入的时候可以根据buf_free来继续上一次写入。

redo日志刷盘和日志文件组

在以下五种情况redo日志会进行刷盘:

1> log buffer空间不足50%的时候
2> 事务提交的时候
3> 后台有一个线程,大约以每秒一次的频率将log buffer中的redo日志刷新到磁盘。
4> 正常关闭服务器时
5> 做checkpoint时

在MySQL的数据目录中,默认有名为ib_logfile0和ib_logfile1的两个文件(可以自定义多个),log buffer中的日志在默认情况下就是顺序刷新到这两个磁盘文件中。

 redo日志文件格式

 因为block的大小是512B,所以设计存储到磁盘上的ib_logfile时也是按照512B为一个单位进行存储;前四个5152B作为预留,保存通用信息

log file header:

  • LOG_HEADER_FORMAT: 4b redo日志的版本
  • LOG_HEADER_PAD1:4b 填充字段,无实际意义
  • LOG_HEADER_START_LSN:8b 标记开始redo日志的LSN,redo日志为空的时候,该值为2048
  • LOG_HEADER_CREATOR:32b 创建者
  • LOG_BLOCK_CHECKSUM:4b 校验用

checkpoint:

  • LOG_CHECKPOINT_NO:记录服务器执行checkpoint的次数,checkpoint用来标记哪些redolog对应的操作已经在磁盘中了,每次都会+1
  • LOG_CHECKPOINT_LSN:记录checkpoint最后的位置,这样在恢复的时候,从checkpoint往后恢复即可
  • LOG_CHECKPOINT_OFFSET:checkpoint的偏移量
  • LOG_CHECKPOINT_LOG_BUF_SIZE:记录logbuffer的大小

最后一个block会指向下一个ib_logfile的第一条记录,即2048位置,最后一个ib_logfile会指向ib_logfile0的记录,所以ib_logfile是循环利用的。

lsn(log sequence number)

  • 一个全局变量,用来记录当前总共已经写入的redo日志量.
  • lsn初始值为8704,也就是说,一条redo日志也没写入的时候,lsn的值就是8704

在计算的时候,lsn会加上header的大小和tailer的大小。

redo日志刷新磁盘长度 

 

buf_next_to_write标记在最后被写入磁盘的mtr末尾,当mtr写入磁盘时,buf_next_to_write会向后移动。

flush链表

redo日志的写入会影响到bufferpool的flush链表中的oldest_modifucation和newest_modification

oldest_modifucation:记录第一次修改的lsn

newest_modification:记录每一次修改的lsn

如下图所示:

 因为flush链表中的控制块会复用,所以mtr3修改了页b,但是o_m没有发生变化,n_m记录每一次修改后的最新lsn,所以会被刷新成10000

刷入磁盘

 当mtr_1从bufferpool被刷入磁盘的时候,此时logbuffer和logfile中的mtr_1已经没有使用的意义了,所以此时logbuffer中的buf_next_to_write会在mtr_1之后,而logfile中的checkpoint也会移动到mtr_1之后。由此可知,在checkpoint之前的所有数据都无意义。

redo日志文件组中各个lsn值的关系

innodb_flush_log_at_trx_commit 

  • 0:表示在事务提交时,不立即向磁盘同步redo日志,这个任务交给后台线程来处理;
  • 1:表示在事务提交时,需要将redo日志同步到磁盘。(默认值)
  • 2:表示在事务提交时,需要将redo日志写到操作系统的缓冲区中,但并不需要保证将日志真正刷新到磁盘。如果操作系统挂掉了,则数据丢失。

 **************此文章只是本人学习过程中的学习笔记,不做其他用途,如果有其他意见,欢迎一起讨论,谢谢,侵删*************************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值