searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

MySQL物理日志REDO

2023-06-08 10:22:40
11
0
MySQL有多种日志。不同种类、不同目的的日志会记录在不同的日志文件中,它们可以帮助你找出mysqld内部发生的事情。比如错误日志:用来记录启动、运行或停止mysqld进程时出现的问题;查询日志:记录建立的客户端连接和执行的语句;二进制日志:记录所有更改数据的语句,主要用于逻辑复制;慢日志:记录所有执行时间超过long_query_time秒的所有查询或不使用索引的查询。而对MySQL中最常用的事务引擎innodb,redo日志是保证事务一致性非常重要的。

前言

为什么要有REDO日志

为了保证数据库的一致性与持久性。为了取得更好的读写性能,InnoDB会将数据缓存在内存中(InnoDB Buffer Pool),对磁盘数据的修改也会落后于内存,这时如果进程或机器崩溃,会导致内存数据丢失,为了保证数据库本身的一致性和持久性,InnoDB维护了REDO LOG。修改Page之前需要先将修改的内容记录到REDO中,并保证REDO LOG早于对应的Page落盘,也就是常说的WAL,Write Ahead Log。当故障发生导致内存数据丢失后,InnoDB会在重启时,通过重放REDO,将Page恢复到崩溃前的状态。
 
为了最大程度避免数据写入时io瓶颈带来的性能问题,MySQL采用了这样一种缓存机制:当query修改数据库内数据时,InnoDB先将该数据从磁盘读取到内存中,修改内存中的数据拷贝,并将该修改行为持久化到磁盘上的事务日志(先写redo log buffer,再定期批量写入),而不是每次都直接将修改过的数据记录到硬盘内,等事务日志持久化完成之后,内存中的脏数据可以慢慢刷回磁盘,称之为Write-Ahead Logging。事务日志采用的是追加写入,顺序io会带来更好的性能优势。
为了避免脏数据刷回磁盘过程中,掉电或系统故障带来的数据丢失问题,InnoDB采用事务日志(redo log)来解决该问题。
 

需要什么样的REDO

1、REDO的数据量要尽量少。REDO的维护增加了一份写盘数据,REDO的写盘时间会直接影响系统吞吐,所以redo的数据量要尽量少
2、REDO的操作要保证幂等。系统崩溃总是发生在始料未及的时候,当重启重放REDO时,系统并不知道哪些REDO对应的Page已经落盘,因此REDO的重放必须可重入。

REDO带来的问题

带来的问题是额外的写REDO Log操作的开销。而为了保证数据的一致性,都要求WAL(Write Ahead Logging)。而 REDO 日志也不是直接写入文件,而是先写入REDO Log Buffer,然后批量刷盘写入日志文件。当需要将日志刷新到磁盘时(如事务提交),将许多日志一起写入磁盘。
WAL 指的是日志比dirty page先落盘, 而不是先写日志再修改page。WAL的语义:刷dirty page时,会等待落盘的flush_to_disk_lsn > page->newest_modification 才会真正写page。在做DML操作时, 先修改buffer pool数据,然后再记录redo log。

REDO日志的管理

在InnoDB内部的日志管理中,一个很重要的概念是LSN(Log Sequence Number),它用来精确记录日志位置信息,且是连续增长的。在InnoDB中,大小为8个字节的值,它的增长量是根据一个MTR(mini-transaction,后面会讲到)写入的日志量来计算的,写多少日志(单位字节),LSN就增长多少。LSN加1,表示日志就多写入一个字节。日志文件轮循一圈(所有日志文件是以循环方式使用的),那么LSN的增长量大约就是整个日志文件的大小(日志文件存在文件头等会占用一部分空间)。它是一个集逻辑意义与物理意义于一身的概念。而在有些数据库中,LSN是一个完全逻辑的概念,每提交一个物理事务,LSN就加1。
在mysql中,LSN, 全局唯一,连续增长
 
那LSN的初始值是多少呢? 在create db时,为 LOG_START_LSN + LOG_BLOCK_HDR_SIZE, 即 16 * 512 + 12 字节(即file header 大小2k + block header大小12字节)
其他情况,请看 srv_start()

REDO组织结构

在InnoDB中,通过日志组来管理日志文件,是一个逻辑定义,包含若干个日志文件,一个组中的日志文件大小和数目可以通过特定的参数设置,可通过innodb_log_file_size 和 innodb_log_files_in_group进行配置 。现在InnoDB只⽀持一个日志组,日志文件会被循环使用。在mysql 8.0中,已经没有日志组的概念了。
REDO日志的写入,都是字节连续的,虽然看上去是多个日志文件,但理解的时候,完全可以把它想象成一个文件,对每一文件掐头去尾,把剩下的空间连接起来,就是总的日志空间了。
s

日志组结构(REDO Log File Group)

目前,InnoDB只支持一个GROUP
日志组与两个变量有关:日志文件的个数、日志文件的大小
日志文件的大小(file_size):记录日志组内每个日志文件的大小,通过参数 innodb_log_file_size 配置,一般设置为1G。 如果不是在.cnf文件里进行配置,log_file_size的值得根据buffer pool 的大小来算,详情请看innodb_log_file_size_init();
日志文件的个数(n_files): 记录这个日志组中的文件个数,通过参数 innodb_log_files_in_group 配置,默认值为2,日志文件名分别为 ib_logfile0 和 ib_logfile1。 如果在启动数据库时,这两个文件不存在,则InnoDB会根据配置参数或默认值,重新创建日志文件。

日志文件结构(REDO Log File)

一个日志组,默认两个日志文件:ib_logfile0, ib_logfile1,每个日志文件由log file header + redo log block组成。每个日志文件都是以Log Block(512字节)为单位进行组织的,前2048字节(4个Block)存放文件头信息,主要用于管理日志内容及整个数据库状态,在这2K内容之后,就是正常的用来存储日志内容的部分。日志文件头占用4个OS_FILE_LOG_BLOCK_SIZE(4 * 512字节)的大小。ib_logfile0 和 ib_logfile1 的文件头信息是不一样的, 5.7 和 8.0 ib_logfile0的文件头信息也是不一样的。头结构定义在 storage/innobase/include/log0log.h
Log File Header Block,是redo log文件的第一个块
mysql 8.0 Log File Header Block结构如下:
log_header_format,占用4个字节,redo log 格式版本号,当前最新版本号为4,如下:LOG_HEADER_FORMAT_5_7_9 = 1, LOG_HEADER_FORMAT_8_0_1 = 2, LOG_HEADER_FORMAT_8_0_3 = 3, LOG_HEADER_FORMAT_8_0_19 = 4, LOG_HEADER_FORMAT_CURRENT = LOG_HEADER_FORMAT_8_0_19
log_header_start_lsn,占用8字节,这个值在初始化及切换redo log 文件时写入
log_header_creator,占用32个字节,值为MySQL 版本号, LOG_HEADER_CREATOR_END - LOG_HEADER_CREATOR
checksum,该block块的checksum值
关于 log file header block 结构填充见源码函数:log_files_header_fill(),log0chkp.cc
 
mysql 5.7 log file header block结构如下:
LOG_GROUP_ID 这个log文件所属的日志组,占用4个字节,当前都是0,表示只有一个group
LOG_FILE_START_LSN 这个log文件记录的开始日志的lsn,占用8个字节;
LOG_FILE_WAS_CRATED_BY_HOT_BACKUP 备份程序所占用的字节数,共占用32字节;
 
checkpoint block
redo log 前4个块中,有两个checkpoint块,这两个checkpoint块有完全相同的结构,如下:
checkpoint_no ,每次checkpoint完成后,递增1。
checkpoint_lsn,lsn值,崩溃恢复从这个位置开始。
lsn_offset,lsn在redo log文件内的偏移,通过函数log_files_real_offset_for_lsn()计算获得。
innodb_log_buffer_size,参数innodb_log_buffer_size的大小,官方文档也没有说明该字段有什么用。
checksum值,该block块的checksum值。
checkpoint 块相关函数:
log_create_first_checkpoint()
log_files_write_checkpoint()

日志块结构(Log Block)

所有WAL是以 Log Block 为单位组织在日志文件中,Block 默认为512字节。所有的日志以 Block 为单位顺序写入文件。Block 包含一个Block Header (12字节)、Block Tailer(4字节,主要是Block内容的crc校验),以及多条 REDO Log Record(最多512 – 12 – 4 = 496字节)。
 
为什么Log Block的大小设计为512字节?
由于历史的原因,考虑到机械硬盘的块大小是512字节,所以日志块大小也设计为512字节。这是为了提高数据库写入吞吐量,如果每次写入是磁盘块大小的倍数,效率才是最高的,并且日志将逻辑事务对数据库的分散随机写入转化成了顺序的512字节整数倍数据的写入(每次写入一到多个Log Block, n * 512字节),这样就可以大大提高数据库的效率。
Block Header
1) log block number 字段:占用日志块最开始的4个字节,表示这是第几个block。 可通过LSN计算得来,计算的函数是log_block_convert_lsn_to_no();
2) block data len 字段: 占两个字节,表示该block中已经有多少个字节被使用; 若是整个块都写满了日志的话,它的长度就是(OS_FILE_LOG_BLOCK_SIZE) 512 字节。
3) First Record offset 字段:占用两个字节,表示该block中作为第一个新的mtr开始log record的偏移量,即第一个全新的 log record 的开始的偏移量。log_block_get_first_rec_group()就是用保存在这个字段的值,获取到此块中第一个新的mtr开始的日志位置。
存在几种特殊情况:
(1)Block中的紧接着Header的就是一个新log record的起始字节,那么first_rec_offset便是Block Header Size,即 12
(2)若Block内的所有有效数据存储的都是上一个Block内最后一个log record的内容,那么first_rec_offset便为0
(3)若log record跨block。 比如MLOG_RENAME 该 log record 跨了两个 Block,其中8字节落在了第二个Block,于是第二个 Block 的 Block Header 字段中的 first_rec_offset 值为12 + 8 = 20。
 
Block Body
也就是中间的496字节,用于存放真正的Redo日志,包含一条或者多条redo log record
 
Block Tailer
Checksum字段:占用四个字节,表示此log block计算出的校验值,用于正确性校验。

日志记录结构(Redo Log Record)

结合MySQL 8.0.19 代码
Innodb的日志是具有逻辑意义的物理日志,所以,日志记录的格式就不完全是物理信息,而是有一定逻辑意义。redo record格式也随着REDO记录不同的作用对象而有所不同,目前有66种redo,可以划分为三大类:
作用于Page的REDO: 比如MLOG_REC_INSERT,MLOG_REC_UPDATE_IN_PLACE,MLOG_REC_DELETE三种类型分别对应于Page中记录的插入,修改以及删除。
作用于Space的REDO: 这类REDO针对一个Space文件的修改,如MLOG_FILE_CREATE,MLOG_FILE_DELETE,MLOG_FILE_RENAME分别对应对一个Space的创建,删除以及重命名。
提供额外信息的Logic REDO:还有少数的几个REDO类型不涉及具体的数据修改,只是为了记录一些需要的信息,比如最常见的MLOG_MULTI_REC_END就是为了标识一个REDO组,也就是一个完整的原子操作的结束。
 
REDO Log的通用结构如下:
虽然不同类型的redo格式可能会不一样,但是所有redo的第一个字节存储的都是固定的,第一个字节:
第一个bit,single_rec_flag,1表示这是单条redo log record组成的mtr,0表示这是多条redo log record组成的mtr
后面7个bit,表示redo log record type
 
作用于Page的REDO
和特定page相关联的redo record,在第一个字节之后跟着的是space_id和page_no,并且都是使用的32位压缩格式进行编码(记录对某个page进行修改的redo log record里通常会包含一些大概率分布在特定范围内的数值,比如标识一个page的space_id和page_no,而针对这种数值,innodb设计了一种简单的编码规则来予以压缩存储:32位压缩格式(占用1~5字节)、64位低压缩率格式(占用5~9字节)、64位高压缩率格式(占用1~11字节),可看 具体压缩方式
这类REDO占所有REDO类型的绝大多数,根据作用的Page的不同类型又可以细分为,Index Page REDO,Undo Page REDO,Rtree PageREDO等。比如MLOG_REC_INSERT,MLOG_REC_UPDATE_IN_PLACE,MLOG_REC_DELETE三种类型分别对应于Page中记录的插入,修改以及删除。这里还是以MLOG_REC_UPDATE_IN_PLACE为例来看看其中具体的内容:
REDO MLOG_REC_UPDATE_IN_PLACE
其中,Type就是MLOG_REC_UPDATE_IN_PLACE类型,Space ID和Page Number唯一标识一个Page页,这三项是所有REDO记录都需要有的头信息,后面的是MLOG_REC_UPDATE_IN_PLACE类型独有的,其中Record Offset用给出要修改的记录在Page中的位置偏移,Update Field Count说明记录里有几个Field要修改,紧接着对每个Field给出了Field编号(Field Number),数据长度(Field Data Length)以及数据(Filed Data)。
 
作用于Space的REDO
这类REDO针对一个Space文件的修改,如MLOG_FILE_CREATE,MLOG_FILE_DELETE,MLOG_FILE_RENAME分别对应对一个Space的创建,删除以及重命名。由于文件操作的REDO是在文件操作结束后才记录的,因此在恢复的过程中看到这类日志时,说明文件操作已经成功,因此在恢复过程中大多只是做对文件状态的检查,以MLOG_FILE_CREATE来看看其中记录的内容:
REDO MLOG_FILE_CREATE 同样的前三个字段还是Type,Space ID和Page Number,由于不是针对Page的操作,这里的Page Number永远是0。在此之后记录了创建的文件flag以及文件名,用作重启恢复时的检查。
space id 使用的64位高压缩率格式编码
 
提供额外信息的 Logic Redo
除了上述类型外,还有少数的几个REDO类型不涉及具体的数据修改,只是为了记录一些需要的信息,比如最常见的MLOG_MULTI_REC_END就是为了标识一个REDO组,也就是一个完整的原子操作的结束。
 
目前 MySQL 8.0.19 版本中有66种 redo record 类型, redo log类型参见enum mlog_id_t, 比较常用的 redo 类型有:
mlog_1byte、mlog_2bytes、mlog_4bytes、mlog_8bytes:这四个类型,表示要某个page在某个位置,写入一个(两个、四个、八个)字节的内容;
mlog_write_string:这种类型的日志,其实和mlog_ibyte是类似的,只是mlog_ibyte是要写一个固定长度的数据,而mlog_write_string是要写一段变长的数据。
mlog_undo_insert:可以简单理解为写undo时候产生的redo
mlog_init_file_page:这个类型的日志比较简单,只有前面的基本头信息,没有data部分;
mlog_comp_page_create:这个类型只需要存一个类型及要创建的页面的位置即可;
mlog_multi_rec_end:这个类型的记录是非常特殊的,它只起一个标记的作用,其存储的内容只有占一个字节的类型值。标识一个mtr产生多条redo记录已经结束,当数据恢复时候,分析mtr时候,只有分析到该类型时候,前面的redo记录才会去做REDO操作.
MLOG_SINGLE_REC_FLAG: 用来标识一个mtr只产生一条redo记录,该标志与一条记录的类型进行"或"运算.当数据恢复时候,判断redo记录是否存在该类型标志.
mlog_comp_rec_clust_delete_mark:这个类型的日志是表示,需要将聚集索引中的某个记录打上删除标志;
mlog_comp_rec_update_in_place:这个类型的日志记录更新后的记录信息,包括所有被更新的列的信息。
mlog_comp_page_reorganize:这个类型的日志表示的是要重组指定的页面,其记录的内容也很简单,只需要存储要重组哪一个页面即可;
0条评论
0 / 1000
yyy
6文章数
0粉丝数
yyy
6 文章 | 0 粉丝
原创

MySQL物理日志REDO

2023-06-08 10:22:40
11
0
MySQL有多种日志。不同种类、不同目的的日志会记录在不同的日志文件中,它们可以帮助你找出mysqld内部发生的事情。比如错误日志:用来记录启动、运行或停止mysqld进程时出现的问题;查询日志:记录建立的客户端连接和执行的语句;二进制日志:记录所有更改数据的语句,主要用于逻辑复制;慢日志:记录所有执行时间超过long_query_time秒的所有查询或不使用索引的查询。而对MySQL中最常用的事务引擎innodb,redo日志是保证事务一致性非常重要的。

前言

为什么要有REDO日志

为了保证数据库的一致性与持久性。为了取得更好的读写性能,InnoDB会将数据缓存在内存中(InnoDB Buffer Pool),对磁盘数据的修改也会落后于内存,这时如果进程或机器崩溃,会导致内存数据丢失,为了保证数据库本身的一致性和持久性,InnoDB维护了REDO LOG。修改Page之前需要先将修改的内容记录到REDO中,并保证REDO LOG早于对应的Page落盘,也就是常说的WAL,Write Ahead Log。当故障发生导致内存数据丢失后,InnoDB会在重启时,通过重放REDO,将Page恢复到崩溃前的状态。
 
为了最大程度避免数据写入时io瓶颈带来的性能问题,MySQL采用了这样一种缓存机制:当query修改数据库内数据时,InnoDB先将该数据从磁盘读取到内存中,修改内存中的数据拷贝,并将该修改行为持久化到磁盘上的事务日志(先写redo log buffer,再定期批量写入),而不是每次都直接将修改过的数据记录到硬盘内,等事务日志持久化完成之后,内存中的脏数据可以慢慢刷回磁盘,称之为Write-Ahead Logging。事务日志采用的是追加写入,顺序io会带来更好的性能优势。
为了避免脏数据刷回磁盘过程中,掉电或系统故障带来的数据丢失问题,InnoDB采用事务日志(redo log)来解决该问题。
 

需要什么样的REDO

1、REDO的数据量要尽量少。REDO的维护增加了一份写盘数据,REDO的写盘时间会直接影响系统吞吐,所以redo的数据量要尽量少
2、REDO的操作要保证幂等。系统崩溃总是发生在始料未及的时候,当重启重放REDO时,系统并不知道哪些REDO对应的Page已经落盘,因此REDO的重放必须可重入。

REDO带来的问题

带来的问题是额外的写REDO Log操作的开销。而为了保证数据的一致性,都要求WAL(Write Ahead Logging)。而 REDO 日志也不是直接写入文件,而是先写入REDO Log Buffer,然后批量刷盘写入日志文件。当需要将日志刷新到磁盘时(如事务提交),将许多日志一起写入磁盘。
WAL 指的是日志比dirty page先落盘, 而不是先写日志再修改page。WAL的语义:刷dirty page时,会等待落盘的flush_to_disk_lsn > page->newest_modification 才会真正写page。在做DML操作时, 先修改buffer pool数据,然后再记录redo log。

REDO日志的管理

在InnoDB内部的日志管理中,一个很重要的概念是LSN(Log Sequence Number),它用来精确记录日志位置信息,且是连续增长的。在InnoDB中,大小为8个字节的值,它的增长量是根据一个MTR(mini-transaction,后面会讲到)写入的日志量来计算的,写多少日志(单位字节),LSN就增长多少。LSN加1,表示日志就多写入一个字节。日志文件轮循一圈(所有日志文件是以循环方式使用的),那么LSN的增长量大约就是整个日志文件的大小(日志文件存在文件头等会占用一部分空间)。它是一个集逻辑意义与物理意义于一身的概念。而在有些数据库中,LSN是一个完全逻辑的概念,每提交一个物理事务,LSN就加1。
在mysql中,LSN, 全局唯一,连续增长
 
那LSN的初始值是多少呢? 在create db时,为 LOG_START_LSN + LOG_BLOCK_HDR_SIZE, 即 16 * 512 + 12 字节(即file header 大小2k + block header大小12字节)
其他情况,请看 srv_start()

REDO组织结构

在InnoDB中,通过日志组来管理日志文件,是一个逻辑定义,包含若干个日志文件,一个组中的日志文件大小和数目可以通过特定的参数设置,可通过innodb_log_file_size 和 innodb_log_files_in_group进行配置 。现在InnoDB只⽀持一个日志组,日志文件会被循环使用。在mysql 8.0中,已经没有日志组的概念了。
REDO日志的写入,都是字节连续的,虽然看上去是多个日志文件,但理解的时候,完全可以把它想象成一个文件,对每一文件掐头去尾,把剩下的空间连接起来,就是总的日志空间了。
s

日志组结构(REDO Log File Group)

目前,InnoDB只支持一个GROUP
日志组与两个变量有关:日志文件的个数、日志文件的大小
日志文件的大小(file_size):记录日志组内每个日志文件的大小,通过参数 innodb_log_file_size 配置,一般设置为1G。 如果不是在.cnf文件里进行配置,log_file_size的值得根据buffer pool 的大小来算,详情请看innodb_log_file_size_init();
日志文件的个数(n_files): 记录这个日志组中的文件个数,通过参数 innodb_log_files_in_group 配置,默认值为2,日志文件名分别为 ib_logfile0 和 ib_logfile1。 如果在启动数据库时,这两个文件不存在,则InnoDB会根据配置参数或默认值,重新创建日志文件。

日志文件结构(REDO Log File)

一个日志组,默认两个日志文件:ib_logfile0, ib_logfile1,每个日志文件由log file header + redo log block组成。每个日志文件都是以Log Block(512字节)为单位进行组织的,前2048字节(4个Block)存放文件头信息,主要用于管理日志内容及整个数据库状态,在这2K内容之后,就是正常的用来存储日志内容的部分。日志文件头占用4个OS_FILE_LOG_BLOCK_SIZE(4 * 512字节)的大小。ib_logfile0 和 ib_logfile1 的文件头信息是不一样的, 5.7 和 8.0 ib_logfile0的文件头信息也是不一样的。头结构定义在 storage/innobase/include/log0log.h
Log File Header Block,是redo log文件的第一个块
mysql 8.0 Log File Header Block结构如下:
log_header_format,占用4个字节,redo log 格式版本号,当前最新版本号为4,如下:LOG_HEADER_FORMAT_5_7_9 = 1, LOG_HEADER_FORMAT_8_0_1 = 2, LOG_HEADER_FORMAT_8_0_3 = 3, LOG_HEADER_FORMAT_8_0_19 = 4, LOG_HEADER_FORMAT_CURRENT = LOG_HEADER_FORMAT_8_0_19
log_header_start_lsn,占用8字节,这个值在初始化及切换redo log 文件时写入
log_header_creator,占用32个字节,值为MySQL 版本号, LOG_HEADER_CREATOR_END - LOG_HEADER_CREATOR
checksum,该block块的checksum值
关于 log file header block 结构填充见源码函数:log_files_header_fill(),log0chkp.cc
 
mysql 5.7 log file header block结构如下:
LOG_GROUP_ID 这个log文件所属的日志组,占用4个字节,当前都是0,表示只有一个group
LOG_FILE_START_LSN 这个log文件记录的开始日志的lsn,占用8个字节;
LOG_FILE_WAS_CRATED_BY_HOT_BACKUP 备份程序所占用的字节数,共占用32字节;
 
checkpoint block
redo log 前4个块中,有两个checkpoint块,这两个checkpoint块有完全相同的结构,如下:
checkpoint_no ,每次checkpoint完成后,递增1。
checkpoint_lsn,lsn值,崩溃恢复从这个位置开始。
lsn_offset,lsn在redo log文件内的偏移,通过函数log_files_real_offset_for_lsn()计算获得。
innodb_log_buffer_size,参数innodb_log_buffer_size的大小,官方文档也没有说明该字段有什么用。
checksum值,该block块的checksum值。
checkpoint 块相关函数:
log_create_first_checkpoint()
log_files_write_checkpoint()

日志块结构(Log Block)

所有WAL是以 Log Block 为单位组织在日志文件中,Block 默认为512字节。所有的日志以 Block 为单位顺序写入文件。Block 包含一个Block Header (12字节)、Block Tailer(4字节,主要是Block内容的crc校验),以及多条 REDO Log Record(最多512 – 12 – 4 = 496字节)。
 
为什么Log Block的大小设计为512字节?
由于历史的原因,考虑到机械硬盘的块大小是512字节,所以日志块大小也设计为512字节。这是为了提高数据库写入吞吐量,如果每次写入是磁盘块大小的倍数,效率才是最高的,并且日志将逻辑事务对数据库的分散随机写入转化成了顺序的512字节整数倍数据的写入(每次写入一到多个Log Block, n * 512字节),这样就可以大大提高数据库的效率。
Block Header
1) log block number 字段:占用日志块最开始的4个字节,表示这是第几个block。 可通过LSN计算得来,计算的函数是log_block_convert_lsn_to_no();
2) block data len 字段: 占两个字节,表示该block中已经有多少个字节被使用; 若是整个块都写满了日志的话,它的长度就是(OS_FILE_LOG_BLOCK_SIZE) 512 字节。
3) First Record offset 字段:占用两个字节,表示该block中作为第一个新的mtr开始log record的偏移量,即第一个全新的 log record 的开始的偏移量。log_block_get_first_rec_group()就是用保存在这个字段的值,获取到此块中第一个新的mtr开始的日志位置。
存在几种特殊情况:
(1)Block中的紧接着Header的就是一个新log record的起始字节,那么first_rec_offset便是Block Header Size,即 12
(2)若Block内的所有有效数据存储的都是上一个Block内最后一个log record的内容,那么first_rec_offset便为0
(3)若log record跨block。 比如MLOG_RENAME 该 log record 跨了两个 Block,其中8字节落在了第二个Block,于是第二个 Block 的 Block Header 字段中的 first_rec_offset 值为12 + 8 = 20。
 
Block Body
也就是中间的496字节,用于存放真正的Redo日志,包含一条或者多条redo log record
 
Block Tailer
Checksum字段:占用四个字节,表示此log block计算出的校验值,用于正确性校验。

日志记录结构(Redo Log Record)

结合MySQL 8.0.19 代码
Innodb的日志是具有逻辑意义的物理日志,所以,日志记录的格式就不完全是物理信息,而是有一定逻辑意义。redo record格式也随着REDO记录不同的作用对象而有所不同,目前有66种redo,可以划分为三大类:
作用于Page的REDO: 比如MLOG_REC_INSERT,MLOG_REC_UPDATE_IN_PLACE,MLOG_REC_DELETE三种类型分别对应于Page中记录的插入,修改以及删除。
作用于Space的REDO: 这类REDO针对一个Space文件的修改,如MLOG_FILE_CREATE,MLOG_FILE_DELETE,MLOG_FILE_RENAME分别对应对一个Space的创建,删除以及重命名。
提供额外信息的Logic REDO:还有少数的几个REDO类型不涉及具体的数据修改,只是为了记录一些需要的信息,比如最常见的MLOG_MULTI_REC_END就是为了标识一个REDO组,也就是一个完整的原子操作的结束。
 
REDO Log的通用结构如下:
虽然不同类型的redo格式可能会不一样,但是所有redo的第一个字节存储的都是固定的,第一个字节:
第一个bit,single_rec_flag,1表示这是单条redo log record组成的mtr,0表示这是多条redo log record组成的mtr
后面7个bit,表示redo log record type
 
作用于Page的REDO
和特定page相关联的redo record,在第一个字节之后跟着的是space_id和page_no,并且都是使用的32位压缩格式进行编码(记录对某个page进行修改的redo log record里通常会包含一些大概率分布在特定范围内的数值,比如标识一个page的space_id和page_no,而针对这种数值,innodb设计了一种简单的编码规则来予以压缩存储:32位压缩格式(占用1~5字节)、64位低压缩率格式(占用5~9字节)、64位高压缩率格式(占用1~11字节),可看 具体压缩方式
这类REDO占所有REDO类型的绝大多数,根据作用的Page的不同类型又可以细分为,Index Page REDO,Undo Page REDO,Rtree PageREDO等。比如MLOG_REC_INSERT,MLOG_REC_UPDATE_IN_PLACE,MLOG_REC_DELETE三种类型分别对应于Page中记录的插入,修改以及删除。这里还是以MLOG_REC_UPDATE_IN_PLACE为例来看看其中具体的内容:
REDO MLOG_REC_UPDATE_IN_PLACE
其中,Type就是MLOG_REC_UPDATE_IN_PLACE类型,Space ID和Page Number唯一标识一个Page页,这三项是所有REDO记录都需要有的头信息,后面的是MLOG_REC_UPDATE_IN_PLACE类型独有的,其中Record Offset用给出要修改的记录在Page中的位置偏移,Update Field Count说明记录里有几个Field要修改,紧接着对每个Field给出了Field编号(Field Number),数据长度(Field Data Length)以及数据(Filed Data)。
 
作用于Space的REDO
这类REDO针对一个Space文件的修改,如MLOG_FILE_CREATE,MLOG_FILE_DELETE,MLOG_FILE_RENAME分别对应对一个Space的创建,删除以及重命名。由于文件操作的REDO是在文件操作结束后才记录的,因此在恢复的过程中看到这类日志时,说明文件操作已经成功,因此在恢复过程中大多只是做对文件状态的检查,以MLOG_FILE_CREATE来看看其中记录的内容:
REDO MLOG_FILE_CREATE 同样的前三个字段还是Type,Space ID和Page Number,由于不是针对Page的操作,这里的Page Number永远是0。在此之后记录了创建的文件flag以及文件名,用作重启恢复时的检查。
space id 使用的64位高压缩率格式编码
 
提供额外信息的 Logic Redo
除了上述类型外,还有少数的几个REDO类型不涉及具体的数据修改,只是为了记录一些需要的信息,比如最常见的MLOG_MULTI_REC_END就是为了标识一个REDO组,也就是一个完整的原子操作的结束。
 
目前 MySQL 8.0.19 版本中有66种 redo record 类型, redo log类型参见enum mlog_id_t, 比较常用的 redo 类型有:
mlog_1byte、mlog_2bytes、mlog_4bytes、mlog_8bytes:这四个类型,表示要某个page在某个位置,写入一个(两个、四个、八个)字节的内容;
mlog_write_string:这种类型的日志,其实和mlog_ibyte是类似的,只是mlog_ibyte是要写一个固定长度的数据,而mlog_write_string是要写一段变长的数据。
mlog_undo_insert:可以简单理解为写undo时候产生的redo
mlog_init_file_page:这个类型的日志比较简单,只有前面的基本头信息,没有data部分;
mlog_comp_page_create:这个类型只需要存一个类型及要创建的页面的位置即可;
mlog_multi_rec_end:这个类型的记录是非常特殊的,它只起一个标记的作用,其存储的内容只有占一个字节的类型值。标识一个mtr产生多条redo记录已经结束,当数据恢复时候,分析mtr时候,只有分析到该类型时候,前面的redo记录才会去做REDO操作.
MLOG_SINGLE_REC_FLAG: 用来标识一个mtr只产生一条redo记录,该标志与一条记录的类型进行"或"运算.当数据恢复时候,判断redo记录是否存在该类型标志.
mlog_comp_rec_clust_delete_mark:这个类型的日志是表示,需要将聚集索引中的某个记录打上删除标志;
mlog_comp_rec_update_in_place:这个类型的日志记录更新后的记录信息,包括所有被更新的列的信息。
mlog_comp_page_reorganize:这个类型的日志表示的是要重组指定的页面,其记录的内容也很简单,只需要存储要重组哪一个页面即可;
文章来自个人专栏
MySQL
6 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0