MySQL-redo日志

为什么要有redo日志?

MySQL以页作为磁盘和内存的交互单位,在需要对数据进行增删改查的时候,会先把记录所在的页面读取到内存中的缓冲区Buffer Pool内,发生增删改都是在缓冲区中进行操作,然后由后台线程定时的把脏页刷新到磁盘中。
因为是异步刷新,数据就可能存在丢失的情况,如何保证数据的持久性呢?
最简单的方法,在对缓冲区的也进行增删改之后,事务一提交,就同步把页面刷新到磁盘中,这样可以保证数据百分之百不会丢失,但是却带来以下问题:

  1. 每个页16KB,如果我们只是修改了一个字节的数据,还是要刷新一整个页。
  2. 每个页在磁盘上都是随机分配的,有一些sql涉及多个页面,众所周知,机械硬盘对随机IO的处理非常慢,对顺序IO的处理很高。
  3. 每次事务提交都要进行刷盘操作,性能大打折扣。
    MySQL当然不可能这样子做,设计者为了解决这个问题,提出了redo日志,redo日志记录每一次发生的更改操作,可以理解成这样记录:
将空间号为0的区域的第13号页偏移量为1000的值更新为1

当然我们这是假设,实际的redo日志有自己的格式,通过这样的日志我们就可以用比较少的空间记录下一次修改。
使用redo日志的好处:

  1. redo日志的一条记录占用空间很少。
  2. redo日志是顺序刷新到硬盘的,机械硬盘处理起来比较快速。

redo日志记录的格式

在这里插入图片描述

  1. type:日志记录的格式,上面我们画的图只是一个通用的格式,MySQL中还有针对各种各样操作不同的记录格式。
  2. space ID:记录所在的空间号。
  3. page number:记录所在的页号。
  4. offset:页内发生修改的偏移量。
  5. 具体数据

Mini-Transaction(MTR)

在事务中一般都有多个sql,我们要保证这多个sql的原子性,因此我们的redo日志中的记录要有组的概念,同一组的数据要么全部恢复,要么全部不恢复以此来对应事务(对于事务,MySQL会生成多条redo的log,这些log不是在事务操作的时候一条一条写到redo日志中的,而是事务提交的时候一并写到日志中)。

MLOG_MULTI_REC_END

在这里插入图片描述
在一个组的记录结尾处都为加上一个MLOG_MULTI_REC_END,这样在系统进行恢复的时候,只有解析到MLOG_MULTI_REC_END,才认为解析到了完整的一组redo日志。然而也不是所有的操作都属于用组来记录,如果只有单个redo日志记录就可以表示的操作,在其后面硬生生的加上一块MLOG_MULTI_REC_END区域,是非常浪费空间的,因此MySQL的设计者把type的第一位拿出来,如果第一位是1,就表示是单条redo日志记录。(因为只要7位就可以包含所有的redo日志的记录类型了,所以有1位刚好可以拿来使用)
无论是单个记录还是组的一堆记录,我们都称为一个MTR。

redo log block

在这里插入图片描述
MySQL的设计者把一个完整的日志文件划分成为多个512字节的页来保存MTR,这样的页称为一个redo block。这样做的目的是让redo log内存和磁盘进行交互的时候可以以512字节的block作为交互单位,增加性能。

redo log日志缓冲区

和页在内存中有一个Buffer Pool缓冲区一样,block在内存中也有一个log buffer的缓冲区,该内存默认大小为16MB。
在这里插入图片描述
记录顺序的写入到log buffer中。

redo日志刷盘时机

保存在log buffer的MTR记录最终还是要刷新到磁盘中才可以防止丢失,在以下情况会刷盘:

  1. log buffer空间不足时
  2. 事务提交时(可以设置)
  3. 后台线程大约一秒一次进行刷新
  4. 服务器正常关闭

innodb_flush_log_at_trx_commit

该变量可以控制是否在事务提交之后,就把redo log刷新到磁盘,有以下值可以设置:

  • 0:表示事务提交不会立刻刷新,而是等待后台线程去刷新,如果后台线程没有及时刷新发生宕机,日志就会丢失。
  • 1:每次事务提交就刷新log到磁盘,是MySQL的默认方式。
  • 2:把数据刷新到系统内存中,这样就算MySQL服务挂了,只要机器不宕机,那么系统内存还有这些log数据。

log sequence number

自系统运行开始,肯定就在不断的发生页面的修改,也就意味着不断地会生成redo log,MySQL的设计者定义了一个lsn(log sequence number)全局变量用来记录已经写入的redo日志量。系统刚启动时,没有任何redo log,lsn的值为8704。

buf_free和flushed_to_disk_lsn

MySQL通过buf_freebuf_next_to_write两个指针来控制MTR加入到log buffer的位置以及下一个要刷新到磁盘的MTR位置,如图所示。
在这里插入图片描述

  1. 假设我们再加一个200自己的MTR,那么buf_free指针就要向前移动200字节,同时lsn也要加上200(我们这里屏蔽了block的header和trailer,实际上每一个block的header和trailer也要加到lsn里面)
  2. 如图所示,MTR1和MTR2是已经刷新到磁盘log文件的,而MTR3和MTR4是下一步要刷新的。

redo文件

MySQL默认会用来个文件来循坏保存我们的redo log文件,如图所示:
在这里插入图片描述
file1放满之后,就会放到file2,file2放满之后又会重新放到file1,那么这样就涉及到redo文件中日志的替换。什么时候可以替换呢?
首先,日志文件也有lsn,他的值是内存保存到磁盘的最大MTR的lsn值。
然后,又有一个checkpoint指针,这个指针指向的是下一个要刷新到磁盘页对应的MTR,也就是指针之前的MTR已经顺利的刷到磁盘的数据文件了,那么这些MTR就是可以删除的。
在这里插入图片描述

checkpoint如何更新

我们之前说过一个flush链表,该链表用来保存所有的脏页的控制块,在控制块中就有oldest_modification和new_modification两个值,表示这个脏页对应MTR的开始lsn和结束lsn,由于脏页是往头部插入链表的,刷新又是从尾部,所以可以保证这些脏页的lsn是不断递增的。

数据恢复

从checkpoint指针开始,往后知道lsn的值就是还没有持久化到数据库的,要从checkpoint开始恢复直到lsn。

查看系统中的lsn值

通过SHOW ENGINE INNNODB STATUS命令可以查看到有关lsn的值:

---
LOG
---
Log sequence number 27470478
Log flushed up to   27470478
Pages flushed up to 27470478
Last checkpoint at  27470469
0 pending log flushes, 0 pending chkp writes
21185 log i/o's done, 0.00 log i/o's/second
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值