InnoDBログファイルの削除。
提供:MySQL Practice Wiki
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ログとして使用される。最後のチェックポイント以降におこなわれた更新を、データファイルへ再適用するのである。ログファイルは全てスキャンされるため、ログファイルが大きいとクラッシュリカバリに時間がかかるようになる。
問題点
性能の問題等でInnoDBのログファイルを削除したい時が多々ある。しかしInnoDBのログファイルはテーブルスペースとセットであり、ログファイルの中にフラッシュされていないデータを残したまま削除してしまうと、InnoDBのテーブルコラプションが発生してしまう。(そうなると復旧方法はmysqldump/リストアしかない。)しかも容易にそのような事態は発生してしまう。
削除手順
要はログファイルの中にフラッシュされていない(テーブルスペースへ書き込まれていない)データが残らないようにすることである。色々と方法があるが、以下の手順がお勧めである。
- まずはデータベースへの更新を止めよう。
- innodb_fast_shutdown=0の設定を行う。
- mysql> SET GLOBAL innodb_fast_shutdown=0
- MySQLサーバをシャットダウンしよう。
- 現在のログファイルをバックアップする。(万が一不具合が起きた時に使用する。)
- ログファイルを削除する。
- サイズを変更したい場合にはmy.cnfを編集しよう。
- MySQLサーバを再起動しよう。するとログファイルが新しく作成されるはずである。