提示:公众号展示代码会自动折行,建议横屏阅读

1 MLOG CHECKPOINT是什么

在MySQL 5.7存储引擎InnoDB崩溃恢复中,我们一定看到过MLOG_CHECKPOIN的身影。从上一个检查点(LOG CHECKPOINT)开始,进行第一次redo日志扫描(参考函数recv_group_scan_log_recs() ),就是要找到MLOG_CHECKPOINT。那么MLOG_CHECKPOINT是用来做什么的?

大家都知道在InnoDB恢复的过程,是先应用redo日志,再执行undo操作。Redo日志从日志文件中读取之后,会按照(space_id, page_no)存入到哈希表中,然后再批量地把日志应用到数据页中。而数据页的读取需要根据space_id来打开对应的数据文件。为此我们写入如下以MLOG_FILE开头的类型日志来记录文件操作:

  1. MLOG_FILE_CREATE2:文件创建时写入,格式为(type, space_id, first_page_no, flags, path)

  2. MLOG_FILE_RENAME2:重命名文件时写入,格式为(type, space_id, first_page_no, path, newpath)

  3. MLOG_FILED_ELETE:文件删除时写入,格式为(type, space_id, first_page_no, path)

  4. MLOG_FILE_NAME(MFN):文件在上一个检查点之后第一次被修改时写入,格式为(type, space_id, first_page_no, path)。Space初始的max_lsn为0,在mtr开始的时候,多了一次函数调用:set_named_space()。在mtr提交的时候,检查如果max_lsn为0,则表示该表第一次被修改,写入MLOG_FILE_NAME日志。同时,修改max_lsn为当前日志系统的lsn。

我们有了这些对应的日志记录,因此在恢复时,只需要扫描一遍日志文件,就可以建立起space id到文件路径的的映射。这里存在一个问题,当系统做检查点的时候,并不能保证,所有修改的数据页对应的MLOG_FILE_NAME日志记录在检查点之后都存在。也就是说检查点可能正好位于某个数据文件对应的MLOG_FILE_NAME日志之后,在该数据文件对应的数据页修改日志记录之前。

如上图所示,检查点-1之后,space_id为100第一次修改,记录了MFN(MLOG_FILE_NAME)日志。新生成的检查点-2之后,有该文件的页面修改日志。如果系统恢复从检查点-2开始,则没有MLOG_FILE_NAME记录,我们就无从得到该数据文件对应的文件路径,对应的数据页日志记录就无法正常应用。

为了弥补这种缺失MLOG_FILE_NAME记录的情况,MLOG_CHECKPOINT应运而生。其基本原理是补充写入MLOG_FILE_NAME,并记录MLOG_CHECKPOINT标记。如下图所示:

在系统做检查点时(参见函数log_checkpoint()),对当前活动的所有数据文件做一个检查点(参见函数fil_names_clear())。扫描所有内存中的space,如果max_lsn大于检查点lsn,那么再次写入对应的MLOG_FILE_NAME日志,并重置max_lsn为0。当这些日志写完之后,写入MLOG_CHECKPOINT,即表示补充的MLOG_FILE_NAME日志结束。因此当系统恢复时,扫描碰到MLOG_CHECKPOINT日志,则意味着,在它之前,检查点之后的所有日志对应的space映射已经完全建立起来了。在它之后,就不存在MLOG_FILE_NAME记录缺失的情况了。

当映射关系建立起来之后,应用日志之前,要对存在哈希表中的日志记录进行预处理(参见函数recv_init_crash_recovery_spaces())。对于文件是删除状态的日志记录,则可以丢弃;对于文件是缺失状态的日志记录,则可以告警或者报错。

这里还有一点需要指出,从检查点开始日志扫描,有可能会扫描到不止一条MLOG_CHECKPOINT日志。每条MLOG_CHECKPOINT日志都记录了对应的检查点LSN,因此必然能找到起始检查点对应的MLOG_CHECKPOINT日志(参见函数recv_parse_log_recs()和变量recv_sys->mlog_checkpoint_lsn),在它之后,最多可能存在一条MLOG_CHECKPOINT日志,即为下一个未完成的检查点写入的。

上图中,如果从检查点-2开始恢复,MLOG_CHECKPOINT-2和其对应的MLOG_FILE_NAME日志是恢复所必须的。MLOG_CHECKPOINT-1和MLOG_CHECKPOINT-3则不是,但也没有副作用。检查点和MLOG_CHECKPOINT之间相互交错,极大地增加了系统恢复的复杂度。

2 MLOG CHECKPOINT的前世今生

在5.6中,是通过扫描所有数据文件,读取第一个数据页获取ID,以此建立space和文件路径的映射关系。

在5.7中,MLOG_CHECKPOINT是在WL#7142: InnoDB: Simplify tablespace discovery during crash recovery中引入。相对于5.6的方案,好处是,不用扫描额外的数据文件(比如孤儿文件等),另外对于没有对应数据文件的日志是否应该丢弃更准确。但是新的方案也引入了新的问题,比如系统检查点逻辑和恢复逻辑变得更复杂,导致了很多新的bug。系统恢复时重复扫描,也导致恢复时间变长。后一个问题其实可以通过将MLOG_CHECKPOINT写入到单独的日志文件中解决。

为了解决这些问题,8.0抛弃了MLOG_CHECKPOINT解决方案。WL#9499 - InnoDB: Replace MLOG_FILE_NAME with MLOG_FILE_OPEN。在新的解决方案中,space到文件路径的映射关系写入到了两个文件tablespaces.open.1和tablespaces.open.2中,同时支持--innodb-scan-directories进行数据文件扫描(如5.6的方式)。新增的两个文件也类似于记录补偿的文件映射补偿日志。笔者当时作为WL#9499的代码审查人之一,深切感受到要把这个看似简单的功能写的稳定可靠,的确要耗费不少心血。

那么除了以上三种方案之外,是否还有其他选择?当然有。通过InnoDB数据字典里space和datafile系统表,就可以建立space到文件的完整映射关系。但是这种方案更加复杂:我们需要在系统恢复之前,先恢复数据字典表。为了先恢复字典表,则需要先扫描日志文件,获取数据字典表的日志信息,优先应用,然后还要进行相应的undo操作。由于字典表的undo记录可能和数据的undo记录在同一事务中,又给字典表单独undo增加了难度。不管是从性能和复杂度来说,都未必比前面三种更优。

综上所述,MLOG_CHECKPOINT已经完成了其在MySQL版本迭代中的历史使命。相比较三种解决方案,5.6的解决方案更简单更可靠。8.0则对5.6和5.7的解决方案进行了综合,也不失为一个好的选择。

参考文档:WL#7142: InnoDB: Simplify tablespace discovery during crash recovery


腾讯数据库技术团队对内支持QQ空间、微信红包、腾讯广告、腾讯音乐、腾讯新闻等公司自研业务,对外在腾讯云上支持TencentDB相关产品,如CynosDB、CDB、CTSDB、CMongo等。腾讯数据库技术团队专注于持续优化数据库内核和架构能力,提升数据库性能和稳定性,为腾讯自研业务和腾讯云客户提供“省心、放心”的数据库服务。此公众号和广大数据库技术爱好者一起,推广和分享数据库领域专业知识,希望对大家有所帮助

文章来源于腾讯云开发者社区,点击查看原文