现象
5.6版本,在创建InnoDB表过程中,若发生crash,会导致服务无法启动。
背景
每个InnoDB表A创建成功后有两个文件A.frm和A.ibd。建表流程如下:
创建A.frm
创建A.ibd
初始化A.ibd
将表A加入InnoDB字典
若crash发生在步骤2之后,则只保留一个完整的A.frm和一个空文件A.idb。
崩溃恢复
在上述的crash发生后,下一次启动则需要做崩溃恢复。崩溃恢复的一个逻辑是需要遍历数据目录下的所有.ibd文件,验证文件与字典的一致性。
对于长度为0的.ibd 文件,报错并跳过,继续检测下一个表。
以上是5.5和5.6共有的逻辑。但5.6的一个新特性破坏了这个规则。
远程目录
5.6支持create table的时候指定其他目录。语法是create table 里新增参数DATA DIRECTORY.这样一个表就可能存在多个表空间。每个表空间对应一个数据结构(fsp_open_info).
这意味着在崩溃恢复过程中,需要验证哪一个表空间是可用的(fil_validate_single_table_tablespace),
验证的方法是尝试读取该表空间的第一个page,若可用则将对应的fsp_open_info::success设置为TRUE。
而在读取本地默认表空间的第一个页时,若碰到读取失败,直接exit(1),导致程序直接退出。“若文件小于4个page就报错”的逻辑,是在这个exit之后。
分析改进
其实在这个场景下,多出来的A.frm和A.ibd并不会导致系统严重问题。由于表A还没有记录入系统字典,实际上只需要将这两个文件直接删掉即可。
因此5.6的这个新增要求过于苛刻。改进方法是将文件大小的判断提前,若发现小于4个page,则直接报错跳过这个表。