redoログとは
redoログは、InnoDBがクラッシュリカバリを実現するための仕組みです。INSERT・UPDATE・DELETEでバッファプール内のページを更新すると、そのページをフラッシュする前に、どのページのどの位置に何を書いたかという変更履歴をredoログに記録します。もしサーバーがクラッシュしても、再起動時にチェックポイント以降のredoログを古い順に適用すれば、ディスクへ反映しきれていなかった変更を復元できます。
B-tree上のページ10のuser_id='user01'のnameをAliceからBobへ更新すると、コミットした直後は次のようになります。バッファプール上のページ10はすでにuser01,Bobですが、データファイル上の同じページはまだuser01,Aliceです。ディスクへ永続化されているのは、ページ10のnameをBobにしたという変更履歴だけです。
LSNとチェックポイント
LSN(Log Sequence Number)は、redoログの論理的な位置を表す番号です。ログはLSNの昇順で生成され、リカバリでもLSNの昇順に適用します。途中を飛ばすことはできません。
チェックポイントLSNは、このLSN以前の変更はすでにディスク上のデータページへ反映されているという境界を表します。チェックポイントより前のログ領域は上書きして再利用することができます。チェックポイントを進めるには、そのLSNまでの変更をすべてデータページへフラッシュしておく必要があります。
ログバッファ
ログバッファは、redoログファイルへ書き出す前にredoログレコードを一時的に置くメモリ領域です。コミットやページ更新で生成されるredoログは、まずここに書かれ、その後バックグラウンドのログライタースレッドがディスクへフラッシュします。多くの行を更新・挿入・削除するトランザクションほどredoログも増えますが、ログバッファを大きくしておくと、コミットまでディスクへの書き込みを待たずにバッファへ溜められ、ディスクI/Oの回数を減らすことができます。
トランザクションの変更がコミットされると、その変更を記述したredoログが生成されます。書き込みは大まかに次の手順です。
- ログバッファ内に書き込む領域(バイト数)を予約する。予約によりstart_lsnからend_lsnの範囲が割り当てられる。
- 予約した範囲へredoログデータを書き込む。
- 書き込み後、「start_lsnからend_lsnまで書き込み済み」という情報をrecent writtenバッファに登録する。
複数スレッドが同時にログバッファへ書くと、書き終わる順序がLSNの並びと一致しないことがあります。たとえばスレッドAがLSN100〜150、スレッドBが150〜200を予約したとき、Bが先に書き終わってAがまだ途中、という状態が起こり得ます。ところがログライターは、リカバリで先頭から順に適用できるように、ディスクへLSNの若い順に書かなければなりません。100〜150がまだ埋まっていないのに150〜200だけをディスクへ書くことはできず、Aの完了を待つ必要があります。
recent writtenバッファは、各スレッドが「自分の範囲を書き終えた」ことを登録する補助構造です。ログライターは、登録されたリンク(start_lsn → end_lsn)の連鎖をたどることで「このLSNより前はすべてバッファに書き込み済み」と判断でき、その境界までをディスクへフラッシュします。リンクが途切れている箇所では該当スレッドの完了を待ちます。これで、複数スレッドの並行書き込みと、ログライターの順序保証が両立します。
ログバッファをいつディスクへフラッシュするかは、innodb_flush_log_at_trx_commitで設定できます。
| 値 | 動作 |
|---|---|
| 0 | 1秒に1回程度フラッシュする。コミット時にはフラッシュしない。クラッシュ時に直近1秒分のコミットが失われ得る |
| 1 | コミットのたびにフラッシュし、OSのfsyncまで完了させる。耐久性が最も高い |
| 2 | コミットのたびにOSのキャッシュへは書くが、fsyncは毎回は行わない。OSがクラッシュするとログが失われ得る |
クラッシュリカバリ
クラッシュ後の再起動時、InnoDBはリカバリ処理を行います。チェックポイントLSNから、ディスク上のredoログに記録された末尾まで、LSNの順にredoログを適用します。まずは各ログレコードに記録された変更履歴に従い、該当ページを読み込んでバッファプール上で変更を反映します。反映されたページはダーティになり、その後のフラッシュでディスクへ書き出されます。こうして、未フラッシュだったダーティページの内容がredoログを通じて復元されます。