--innodb-fast-shutdown
出典: MySQL Practice Wiki
| ファイル | my.cnf, my.ini |
| セクション | [mysqld] |
| データ型 | 整数 |
| デフォルト値 | 1 |
| 値の範囲 | 0, 1, 2 |
| 効果 | 記事を参照 |
| 効果の有効範囲 | mysqld/innodb |
| 使用頻度 | 低 |
説明
InnoDBのログファイルは、別名WAL - Write Ahead Logと呼ばれるもので、名前を日本語に直すと「前もって書き込んでおくためのログ」とでも呼べるだろうか。InnoDBのテーブルに対して行われた更新は、全ていったんログに書き込まれるのである。トランザクションがコミットされると、innodb_flush_log_at_trx_commit=1 が設定されていればログファイルに書き込みが行われる。0または2の場合には、ログバッファと呼ばれる領域にデータが保持される。その後、時間をおいてからテーブルスペースへ更新が反映されるのである。
なぜこのような仕組みになっているのだろうか?
コミットと同時にテーブルスペースを反映すればいいじゃないか?と思われるかも知れない。しかし、テーブルスペースへの更新には時間がかかるのである。なぜか?簡単に言うと、更新するべき箇所がたくさんあるからである。テーブルスペースに含まれているのはデータだけではなく、インデックス情報も含まれている。インデックスはBツリー構造になっているため、場合に因ってはインデックスを辿ったり、ページが分かれる場合にはリーフノード以外にも1つ以上のノンリーフノードを更新しなければいけない。そして、大抵の場合一つのテーブルに定義されているインデックスは複数あり、インデックスが増えればそれだけ更新するべきページも増えてしまう。さらに、一つのトランザクションにおいて更新するテーブルが複数ある場合も多い。更新するページはテーブルスペース内に離散して格納されているので、ディスクに対するランダムアクセスが発生してしまう。
つまり、テーブルスペースの更新はとてもコストが高いので、コミット時にこれら全ての更新を行っていたのでは書き込み性能が出ないのである。
そこで、まずはコミット時に全ての更新をWALに書き込み、時間が経ってからテーブルスペースを更新するという方法が採られている。この方法にはさらに次のメリットがある。
- WALに対する書き込みはシーケンシャルなので、シーク時間がなくとても速い。
- 急激にたくさんの書き込み要求が来た場合に耐えることができる。
- 複数のトランザクションが同じページを更新した場合、書き込み回数を減らすことができる。
即ち、WALにはテーブルスペースにまだ反映されていないデータが含まれていることになる。つまり、InnoDBのデータは、テーブルスペースとログを合わせて初めて、完全な情報を含んでいるのである。なのでみだりにログを削除してはいけない。
MySQL サーバを普通にシャットダウンした場合でも、WALにはテーブルスペースに反映されていないデータが残っている。シャットダウンと同時にテーブルスペースへデータを反映させるには、innodb_fast_shutdown=0を設定すれば良い。このオプションはMySQLサーバ稼働中に変更できるので、以下のようにしてからシャットダウンすると、WALを削除しても安全な状態になる。
mysql> SET GLOBAL innodb_fast_shutdown=0;
MySQL サーバ稼働中、WALにだけ書き込まれたデータはどこにあるのだろうか?答えは、InnoDBバッファプールである。バッファプールはテーブルスペースに対するキャッシュ+その他のデータを保持する領域であるが、対応するページがバッファプール内でだけ更新され、テーブルスペースは更新されていないという状態になる。このとき、そのページは「ダーティ」な状態であるという。
ダーティなページをテーブルスペースへ書き込む動作は、チェックポイント処理と呼ばれる。チェックポイントが行われると、「このページのチェックポイントしたよ!」という情報が、WALに書き込まれる。
また、InnoDBのログファイルは、クラッシュリカバリ時にRedoログとして使用される。最後のチェックポイント以降におこなわれた更新を、データファイルへ再適用するのである。ログファイルは全てスキャンされるため、ログファイルが大きいとクラッシュリカバリに時間がかかるようになる。
このオプションはMySQLサーバーがシャットダウンする際に、InnoDBのログファイルがどのように処理されるかを決定する。取り得る値は0, 1, 2の3種類。
- 0
- ログの内容を完全にテーブルファイルへフラッシュする。最も安全だがとても時間がかかる。
- 1
- いくつかの操作をスキップする。終了前にチェックポイント処理を行うため再起動時にリカバリが走らない。
- 2
- ログの内容をファイルへ反映することだけを保証する。再起動後にはクラッシュリカバリが行われる。
よほどのことがない限りデフォルトの1でいいだろう。
ソースコードの話
sql/ha_innodb.ccの中でinnobase_fast_shutdownに代入され、mysqld稼働中は値がそこに保持される。最終的に、シャットダウンする段階になってsrv_fast_shutdownという変数に値が渡される。実際のシャットダウン時の処理はsrv_fast_shutdownを判定することにより分岐する。srv_fast_shutdownはinnobase/srv/srv0srv.cで定義されている。1と2の違いはlog/log0log.c、0と1の違いはsrv/srv0srv.cを見ると分かる。

