现象

    5.6版本,在创建InnoDB表过程中,若发生crash,会导致服务无法启动。

    背景

    每个InnoDB表A创建成功后有两个文件A.frm和A.ibd。建表流程如下:

    1. 创建A.frm

    2. 创建A.ibd

    3. 初始化A.ibd

    4. 将表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,则直接报错跳过这个表。