redo log概念
redo log是用来保证持久性的日志文件,这样能够保证数据的持久性不会出现数据丢失的情况。
- redo日志占用的空间非常小:存储表空间ID、页号、偏移量以及需要更新的值所需的存储空间是很小的,关于redo日志的格式我们稍后会详细唠叨,现在只要知道一条redo日志占用的空间不是很大就好了。
- redo日志是顺序写入磁盘的:在执行事务的过程中,每执行一条语句,就可能产生若干条redo日志,这些日志是按照产生的顺序写入磁盘的,也就是使用顺序IO。
redo日志格式
其实redo log为了应对不同的场景,有很多不对的redo log但是大多数情况下我们都适用通用的这种redo log结构:
- type:该条redo日志的类型。(在mysql5中大概有50多种类型)
- space ID:表空间ID。
- page number:页号。
- data:该条redo日志的具体内容。
其实redo日志有各种各样的格式,还有一堆概念,我们去读它其实也没什么意思,除非我们需要自研才会去熟读它,其实我们只要了解redo日志可以帮助我们恢复数据就够了。
redo日志的写入过程
redo log back
设计InnoDB的大叔为了更好的进行系统崩溃恢复,他们把通过mtr生成的redo日志都放在了大小为512字节的页中。为了和我们前边提到的表空间中的页做区别,我们这里把用来存储redo日志的页称为block。一个redo log block的示意图如下:
真正的redo日志都是存储到占用496字节大小的log block body中,图中的log block header和log block trailer存储的是一些管理信息。我们来看看这些所谓的管理信息都是啥:
- LOG_BLOCK_HDR_NO:每一个block都有一个大于0的唯一标号,本属性就表示该标号值。
- LOG_BLOCK_HDR_DATA_LEN:表示block中已经使用了多少字节,初始值为12(因为log block body从第12个字节处开始)。随着往block中写入的redo日志越来也多,本属性值也跟着增长。如果log block body已经被全部写满,那么本属性的值被设置为512。
- LOG_BLOCK_FIRST_REC_GROUP:一条redo日志也可以称之为一条redo日志记录(redo log record),一个mtr会生产多条redo日志记录,这些redo日志记录被称之为一个redo日志记录组(redo log record group)。LOG_BLOCK_FIRST_REC_GROUP就代表该block中第一个mtr生成的redo日志记录组的偏移量(其实也就是这个block里第一个mtr生成的第一条redo日志的偏移量)。
- LOG_BLOCK_CHECKPOINT_NO:表示所谓的checkpoint的序号
- log block trailer:检验合法性。
日志缓冲区
由于redo log直接和磁盘进行交互的话也是很不友好的,所以我们也在中间加了一层缓冲区, redo log buffer(若干个连续的redo log block组成)。我们可以通过启动参数innodb_log_buffer_size来指定log buffer的大小,在MySQL 5.7.21这个版本中,该启动参数的默认值为16MB。
redo日志文件
redo日志刷盘时机
- log buffer空间不足时,大概是纵容量的一半左右会进行刷盘,容量大小是可以设置的。
- 事务提交时,虽然事务提交时候可以不把数据刷新到磁盘,但是为了保证持久性,事务提交时候我们把数据刷新到磁盘
- 后台线程不停的刷刷刷,后台有一个线程,大约每秒都会刷新一次log buffer大的redo日志到磁盘
- 正常关闭服务器时
- 做所谓的checkpoint时
- 其他的一些特殊情况
redo日志文件组
其实redo文件是以组的形式存在的,一个redo文件组包括(ib_logfile0,ib_logfile1,ib_logfile2,..... ib_logfilen). 总共的redo日志文件大小其实就是:innodb_log_file_size × innodb_log_files_in_group。
redo日志文件格式
log buffer本质上是一片连续的内存空间,被划分成了若干个512字节大小的block。将log buffer中的redo日志刷新到磁盘的本质就是把block的镜像写入日志文件中,所以redo日志文件其实也是由若干个512字节大小的block组成。
日志文件组中的每个大小都一样格式也一样都是由两部分组成:
- 前2048字节也就是前4个block事用来管理信息的
- 从2048字节之后事用来存储log buffer中的block镜像的。
大概示意图如下:
下面我们来说说前4个block到底是做什么的。
Log Sequence Number
Log Sequence Number是一个全局的变量,就是日志序列号简称lsn。(默认最小8704)。 我们知道在想log buffer中写入redo日志的时候不是一条条的写入的,是以一个mtr生成的一组redo日志为单位写入的。而且实际上是把日志内容写在了log block body处。但是在统计lsn的增长量时,是按照实际写入的日志量加上占用的log block header和log block trailer来计算的。每一组由mtr生成的redo日志都有一个唯一的LSN值与其对应,LSN值越小,说明redo日志产生的越早。
flushed_to_disk_lsn
redo日志是首先写到log buffer中,之后才会被刷新到磁盘上的redo日志文件。所以设计InnoDB的大叔提出了一个称之为buf_next_to_write的全局变量,标记当前log buffer中已经有哪些日志被刷新到磁盘中了。画个图表示就是这样:
我们前边说lsn是表示当前系统中写入的redo日志量,这包括了写到log buffer而没有刷新到磁盘的日志,相应的,设计InnoDB的大叔提出了一个表示刷新到磁盘中的redo日志量的全局变量,称之为flushed_to_disk_lsn。系统第一次启动时,该变量的值和初始的lsn值是相同的,都是8704。随着系统的运行,redo日志被不断写入log buffer,但是并不会立即刷新到磁盘,lsn的值就和flushed_to_disk_lsn的值拉开了差距。
flush链表中的lsn
flush链表中的脏页按照修改发生的时间顺序进行排序,也就是按照oldest_modification代表的LSN值进行排序,被多次更新的页面不会重复插入到flush链表中,但是会更新newest_modification属性的值。