概述

WAL文件

MySQL WAL日志是一组日志文件集合,它们在数据库实例创建时预创建,并在数据库运行中被循环使用。WAL文件大小和数目可以通过参数设置,见innodb_log_file_size 和 innodb_log_files_in_group 。

日志文件

每个WAL文件的前2048字节存放文件头信息。文件头后面是WAL内容,按照BLOCK为单位分割,BLOCK大小默认为512字节。日志文件布局如下图所示:

其中几个重要的字段:

  • LOG_GROUP_ID:该log文件所属的日志组,占用4个字节,当前均为0;
  • LOG_FILE_START_LSN: 该log文件记录的开始日志的lsn,占用8字节;
  • LOG_FILE_WAS_CRATED_BY_HOT_BACKUP 备份程序所占用的字节数,共占用32字节

日志块结构

所有WAL是以日志块为单位组织在日志文件中,日志块默认为512字节。所有的日志以块为单位顺序写入文件。每一条记录都有自己的LSN(log sequence number, 表示从日志记录创建开始到特定的日志记录已经写入的字节数)。每个日志块包含一个日志头(12字节)、一个尾部(4字节,主要是Block内容的crc校验),以及一组日志记录(最多512 – 12 – 4 = 496字节) 。

wal_block_format

日志块头包含以下几个字段:

  • block_number:4B,表示这是第几个block块。 可通过LSN计算: log_block_convert_lsn_to_no
  • block_data_len:2B,表示该block中已经写入的字节数,若是整个块都写满了的话,该值是 512
  • first_rec_offset:2B,表示该block中第一个全新的log record的开始log record的偏移量
  • checksum:4B,此block数据校验和,用于正确性校验

这里需要特别解释的是first_rec_offset字段:在innodb事务层产生的wal日志其实是被组织成为一条条的log record,每个record都有特定的类型以及相应的内容。当log record被存储至日志文件中时,可能会出现一个log record跨Block存储的情况,因而需要在Block Header中存储first_rec_offset来代表该Block中第一个起始log record在该Block内的偏移,需要注意的是:first_rec_offset包含Block Header的大小。如下例:

该例中,MLOG_RENAME该log record跨了两个Block,其中8B落在了第二个Block,于是第二个Block的Block Header字段中的first_rec_offset值为12 + 8 = 20。

存在两种特殊情况:1. Block中的紧接着Header的就是一个新log record的起始字节,那么first_rec_offset便是Block Header Size,即12;2. 若Block内的所有有效数据存储的都是上一个Block内最后一个log record的内容,那么first_rec_offset便为0。

LSN和文件偏移量之间转换

在Innodb中LSN是一个非常重要的概念,表示某个log record从日志记录创建开始已经写入的字节数。LSN的计算是包含BLOCK的头和尾字段的。

那如何由一个给定LSN的日志,在日志文件中找到它存储的位置(文件以及在文件内的偏移)是一个比较有意思的问题,原理也比较简单,感兴趣的朋友可以研究下函数log_files_real_offset_for_lsn,这里便不再赘述。