深入浅出InnoDB MLOG CHECKPOINT
提示:公众号展示代码会自动折行,建议横屏阅读
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 , new path )
3) MLOG_ FILE D_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等。 腾讯数据库技术团队专注于持续优化数据库内核和架构能力,提升数据库性能和稳定性,为腾讯自研业务和腾讯云客户提供“省心、放心”的数据库服务。 此公众号和广大数据库技术爱好者一起,推广和分享数据库领域专业知识,希望对大家有所帮助