WAL(先行書き込みログ) — 台帳を書き換える前に、裏紙にメモする
銀行の振込は「Aさんから1000円引く」「Bさんに1000円足す」の2手でできています。もし1手目と2手目のあいだで停電したら? 台帳にはAさんから引いた記録だけが残り、1000円がどこにも存在しなくなります。
データベースはこれを、台帳を書き換える前に、まず裏紙にメモすることで防いでいます。この裏紙がWAL(Write-Ahead Log:先に書くログ)です。下の図1で振込を1手ずつ進め、好きなタイミングで停電させてみてください。
WALなし — 途中の状態が、そのまま台帳に残る
台帳(データファイル)を直接書き換えるやり方では、振込の1手目を書いた瞬間に、ディスクの上は「引いたけど足していない」状態になります。図1で1手目のあとに停電させると、再起動しても台帳しか手がかりがありません。何をやりかけていたのか分からないので、直しようがないのです。
しかもこのやり方は遅い。台帳のAのページ、Bのページ……と、ディスクのあちこちをその場で書き換えるからです。かといって速くするために「メモリで書き換えておいて、あとでまとめて台帳へ」とすると、今度は停電で完了したはずの振込まで消えます。速さと安全が、真正面からぶつかります。
WALあり — 先にメモ、確定は COMMIT の1行で
WALのルールはひとつだけ。台帳を変える前に、何をするかをログに書く。ログは末尾に書き足していくだけなので、あちこち書き換える台帳よりずっと速くディスクに書けます。振込の最後に COMMIT という1行を書き、それがディスクに届いた時点で「完了」と答えます。台帳への書き戻しは後回しでかまいません。
停電したら、再起動時にログを頭から読み直します。COMMIT まで書かれた振込は、メモを見ながら台帳にやり直す(REDO)。COMMIT のない書きかけは、なかったことにする。こうして台帳は必ず「振込が全部済んだ」か「まったくしていない」のどちらかに戻ります。トランザクションの原子性と永続性(ACIDのAとD)を支えているのは、この裏紙です。
ログが無限に伸びないよう、ときどきチェックポイントでメモリの変更を台帳にまとめて書き戻します。そこまでのログは反映済みになり、再起動のときに読み直すのはチェックポイント以降だけで済みます。
- WAL(先行書き込みログ)
- データを変える前に、変更内容をログに追記しておく仕組み。PostgreSQLのWAL、MySQL(InnoDB)のredo logなど。
- COMMIT
- トランザクションの確定。WALではCOMMITの記録がディスクに届いた時点で完了とみなす。
- fsync
- OSのキャッシュに留まっている書き込みを、ディスクまで確実に届けさせる命令。
- REDO(やり直し)
- クラッシュ後、ログに残った確定済みの変更を台帳に再適用すること。
- チェックポイント
- メモリ上の変更をデータファイルに書き戻し、それ以前のログを不要にする区切り。
まとめ
WALは「台帳より先に、裏紙にメモする」というだけの約束です。メモは末尾に書き足すだけなので速く、COMMIT の1行があるかないかで確定したかどうかがはっきり分かる。だから停電しても、完了した振込は再生でき、書きかけは捨てられます。データベースが「完了」と答えたものを失わず、しかも速く動けるのは、このメモ帳のおかげです。