障害回復とログ — クラッシュしても失わない

停電やプロセス落ちがいつ来ても、コミット済みは残し、未コミットは無かったことにする。その約束を果たす仕組みがログ先行書き込み(WAL)。変更はデータより先にログへ書く。再起動時に、コミット済みをREDOで塗り直し、未コミットをUNDOで巻き戻す。走査を短くするチェックポイントまで、クラッシュを起こして回復する様子を動かして掴みます。

1. データより先にログを書く — WAL

変更はまずメモリ上のページに書かれ、いつディスクへ流れるかはバッファ管理任せ。ここで停電が来たら? 鍵はログ先行書き込み(WAL) — そのページをディスクへ書く前に、変更を記したログレコードを先にディスクへ。特にコミット時はコミットログを強制的にディスクへ吐き出す。こうすれば「ログに在るのにデータに無い」はREDOで直せ、「データに在るのにログに無い」は起こさない。

下で1件の更新(X: 100→150)の流れを動かします。いまクラッシュを押すと、その瞬間の状態から回復結果が決まります。WAL規則を外すと、ログより先にデータをディスクへ書いてしまい、UNDOできない不整合が生まれる危険が見えます。

ログ先行書き込み — クラッシュの瞬間で運命が決まる
左=メモリ(バッファプール)、中央=ディスクのログ、右=ディスクのデータ。飛ぶ点が「いま書き込んでいる先」。WALありなら必ずログ→データの順。クラッシュ時、コミットログが届いていればREDOで150を復元(永続性)、未達なら変更なしで巻き戻し(原子性)。WAL違反でデータだけ先に届くと、ログ無し=UNDO不能で不整合に陥ります。
POINT — WALの2つの黄金律 ①UNDO則:変更したデータページをディスクへ書く前に、そのログ(前の値を含む)を先にディスクへ。②REDO則:トランザクションをコミット確定とする前に、そのコミットログをディスクへ強制。この2つを守れば、いつ落ちても「コミット済みは残り、未コミットは消える」を再現できる。ログはシーケンシャル追記なので高速だ。
コミット確定 ⟺ コミットログがディスクに到達 コミット応答を返した=ログが不揮発ストレージに載った、の意味。データページはまだメモリでよい(後でREDOが面倒を見る)。

2. ログには何が書いてあるか — 前の値と後の値

各更新はログレコードとして追記されます。中身はLSN(通し番号)・トランザクションID・対象・変更前の値(before)・変更後の値(after)。この2つの値が回復の材料です — 巻き戻し(UNDO)には前の値、塗り直し(REDO)には後の値を使う。そしてコミットレコードが書かれた瞬間が「永続化の境界線」です。

ログレコードの中身 — UNDOは前の値、REDOは後の値
上から時間順にログが追記されます。各書込レコードは前の値→後の値を持つ。UNDO視点では前の値の列が光り「後→前へ戻す材料」、REDO視点では後の値の列が光り「前→後へ塗り直す材料」。緑のコミット行から下が確定=永続の境界です。
POINT — 1本のログで前にも後ろにも直せる before-image があるから、未コミットの変更を逆再生して消せる(UNDO)。after-image があるから、コミット済みだがディスク未反映の変更を順再生して復元できる(REDO)。ログは追記オンリーの一本道だが、そこに過去と未来の両方を作り直す情報が詰まっている。

3. クラッシュからの回復 — REDOとUNDO

再起動時、ログを見て各トランザクションを2組に仕分けます。クラッシュ前にコミット済み=勝者、まだ未コミット=敗者。勝者はREDOで前向きに塗り直し(ディスクに届いていない変更を復元)、敗者はUNDOで後ろ向きに巻き戻す(漏れ出た変更を消す)。両方を終えると、ちょうど「コミット済みだけが反映された」一貫状態に戻ります。

下でクラッシュ位置を動かすと、勝者・敗者の顔ぶれが変わります。回復を実行すると、REDO(前へ)→ UNDO(後ろへ)の走査が動き、下のデータ項目が正しい値へ収束します。

仕分けて直す — 勝者REDO・敗者UNDO
上段=各トランザクションの区間(●書込・◆コミット)。赤縦線=クラッシュ。線より左でコミットした勝者は緑、未コミットの敗者は赤。下段のデータ A〜E は、クラッシュ時にディスクへ中途半端に漏れた状態。REDOで勝者の変更を復元し、UNDOで敗者の変更を消すと、右の「一貫状態」へ揃います。
注意 — REDOは冪等でなければならない 回復の途中でまた落ちることもある。だからREDO/UNDOは何度繰り返しても同じ結果(冪等)である必要がある。各ページに最後に適用したログ番号(LSN)を記録し、すでに反映済みのログはスキップする。ARIESの「まず履歴を丸ごと繰り返す(repeating history)」設計は、この冪等性の上に成り立っている。

4. どこまで遡るか — チェックポイント

ログは延々と伸びます。回復のたびに最初から全部走査していては遅い。そこで定期的にチェックポイントを打ち、その時点でダーティなページをディスクへ書き出し、稼働中トランザクションを記録します。すると回復は直近のチェックポイントから先だけを見ればよくなる — それより前のコミット済み変更は、もうディスクに載っているからです。

走査範囲を区切る — チェックポイントの効果
緑の縦線=チェックポイント、赤=クラッシュ。左のグレー帯は走査不要(もうディスクに永続化済み)、右の明るい帯だけがREDO走査範囲。チェックポイントを右に動かすほど走査が短くなり、回復が速くなります。ただし打つ頻度を上げると通常時の負荷も上がる — 回復時間と平常時コストのトレードオフです。
POINT — チェックポイントは「安全な足場」 それは「ここまでのコミット済み変更は確実にディスクにある」という保証線。回復はここから前向きにREDOを始め、まだ生きていた敗者だけをUNDOすればよい。ファジーチェックポイントを使えば処理を止めずに足場を打てるので、実DBは走らせたまま定期的にこれを刻んでいる。
回復のREDO走査 = [直近チェックポイント, クラッシュ] だけ それより古いログは読み飛ばせる。チェックポイント間隔が回復時間の上限をほぼ決める。

5. まとめ — 失わないための約束

一歩先へ — ARIESという定番設計 現代のDBが土台にするのがARIES。回復は3段階 — 解析(Analysis)でチェックポイント以降を読み勝者・敗者とダーティページを把握し、REDOで「歴史を丸ごと繰り返して」クラッシュ直前の状態を再現し、UNDOで敗者だけを巻き戻す。巻き戻し自体もログに補償ログレコード(CLR)として残すので、UNDOの途中で再クラッシュしても二度手間にならない。ログ番号(LSN)を各ページに刻む一貫した規律が、この複雑な舞台を破綻なく回している。