innodb_data_file_pathについてのメモ

昨日一度エントリを書きだしつつ途中で面倒くさくなって破棄したのですが、やっぱり簡単にメモだけ残しておきます。残さないと半年後くらいにサッパリ忘れそうでw

制限事項まとめ

先に制限事項をまとめておきます

その1

  • innodb_data_file_pathに列挙できるデータファイルの定義数は最大で999まで。
  • 1000個以上のデータファイル定義を行うと、mysqldの起動時のinnodb初期処理で意図的にエラーが返されて、innodbが停止した状態でmysqldが起動します。

その2

  • innodb_data_file_path=file1:1G;file2:1G ... という具合に列挙していった際につらつら書けるのは4096bytes未満。
  • 4096bytesを越えた分は改行してなくても改行したと見なされて(多くのケースでは)my.cnfパース時にエラーになりmysqldが起動しません。

最大999であること

innobase/srv/srv0start.cのopen_or_create_data_file関数にて

        if (srv_n_data_files >= 1000) {
                fprintf(stderr, "InnoDB: can only have < 1000 data files\n"
                                "InnoDB: you have defined %lu\n",
                                (ulong) srv_n_data_files);
                return(DB_ERROR);
        }

このようにsrv_n_data_files(innodb_data_file_pathに定義されたデータファイルの数)が1000以上だと意図的にエラーを返す実装になっています。

innodb_data_file_pathで指定した定義文字列のパースは同じくsrv0start.cのsrv_parse_data_file_paths_and_sizes関数にて行われています。

システム変数の定義文字列は4096bytes未満しか書けないこと

これはInnoDBの制限ではなくMySQLの制限です。my.cnfの利用上の制限というべきか。

my.cnfファイルのパースはmysys/default.cのsearch_default_file_with_ext関数にて行われます。my.cnfファイルの読み込みは以下のようにfgets(3)関数を使用して行っています。

  if (!(fp= my_fopen(name, O_RDONLY, MYF(0))))
    return 1;                                   /* Ignore wrong files */

  while (fgets(buff, sizeof(buff) - 1, fp))
  {
    line++;

C標準関数であるfgets(3)はmanで確認すると、以下のような仕様になっています。

fgets() は stream から最大で size - 1 個の文字を読み込み、 s が指すバッファに格納する。読み込みは EOF または改行文字を読み込んだ後で停止する。読み込まれた改行文字はバッファに格納される。 ’\0’ 文字が一つバッファの中の最後の文字の後に書き込まれる。

ここではsizeof(buff) -1 を渡していますから、読み込んでくれる最大サイズはbuffの長さに依存するのですが、buffの定義を見ると以下のように配列サイズが4096とハードコーディングされています。

  char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext;
  char *value, option[4096], tmp[FN_REFLEN];

もちろん、search_default_file_with_ext関数がもし「読み込んだバッファ内に改行コード等の終端を意味する制御記号等がなかったら次に読み込んだバッファを連結してひとつのシステム変数の定義として処理する」というようなロジックになっていれば、4096bytes未満という制限はないかもしれませんが、実際のsearch_default_file_with_ext関数はそういった配慮をするロジックはありません。

要するに「fgets(3)が読み込んだ単位に基づいて1つの定義と見なす」という実装になっているということです。

これにより、4096bytes未満という制限が発生しています。

ちなみに、buff、curr_gr、optionの配列サイズを弄るとこの制限を変更することができました。

まとめ

innodb_data_file_pathに多数のデータファイル定義を行う必要がある場合には、上記制限にひっかからないようにしましょう。

特に、数百個のデータファイル定義を行う場合には、999個制限にはひっかかりませんが4096bytes制限にはひっかかるようなったりするので、その場合は(ちょっと格好わるいですが)データファイル名を短くするなどして制限にひっかからないようにしましょう。