データベースのWALとは?COMMITの裏側とクラッシュリカバリを初心者向けに解説

データベース

「COMMITした結果は停電しても消えない(=永続性)」とよく言われますが、その仕組みを正確に説明できますか?この記事では、永続性を支える主役である WAL(先行書き込みログ) を中心に、バッファプール・fsync・チェックポイント・クラッシュリカバリといったデータベースの内部動作を図解でやさしく解説します。

SELECT / UPDATE は書けて、トランザクションもなんとなく分かってきた初心者エンジニアの方が対象です。読み終えると「変更はまずRAMとログ、本体への反映は後回し」という直感が身につきます。


背景:ディスクは遅く、しかも途中で電源が落ちる

データの本体はディスク(SSD/HDD)にありますが、ディスクへの書き込みはメモリ(RAM)に比べてとても遅い処理です。もし UPDATE のたびに巨大なデータ本体をディスクの正しい場所へ書き込んでいたら、遅すぎて使い物になりません。

さらに厄介なのが、書き込みの途中で電源が落ちる可能性です。データ本体を書きかけのままクラッシュすると、データが中途半端に壊れてしまいます。「速さ」と「壊れない保証」を両立させるための工夫が、これから説明する仕組みです。


図解:データはまずRAM、確定はログへ

SQLの読み書きは、いきなりディスクをいじるのではなく、RAM上のキャッシュ(バッファプール)を経由します。そして変更の記録は、データ本体より先にWAL(ログ)へ書き込まれます。

読み書きはまずRAM(バッファプール)で行い、変更はデータ本体より先にWALログへ書き込みます。


用語をひとつずつ:バッファプール・WAL・fsync・チェックポイント

この節では、WALの仕組みを理解するうえで欠かせない4つの用語を順番に説明します。

バッファプール(RAM上のキャッシュ)

よく使うデータのページをRAM上に置いておくキャッシュです。SELECT は、まず欲しいページがバッファプールにあるか確認します。あれば即座に返せます(キャッシュヒット、とても速い)。なければディスクから読み込みます(キャッシュミス、ここで遅い読み込みが発生)。UPDATE も同じで、まずRAM上のページを書き換えます。この「変更されたがまだディスクに反映していないページ」をダーティページと呼びます。

WAL(先行書き込みログ)

WAL(Write-Ahead Logging、ログ先行書き込み)とは、データの変更をデータ本体に反映する前に、その変更内容をログとして先に書き込んでおく仕組みです。名前のとおり「ログを先に(Ahead)書く」のが核心です。

COMMIT が実行されると、データベースはその変更を記録したWALをディスクへ確実に書き込みます。重い「データ本体」の更新を待つのではなく、軽い「ログ」さえディスクに残せば、たとえ直後に電源が落ちても、ログから変更を復元できます。これが「COMMITした結果は消えない(永続性)」の正体です。

なぜログだと速いのか
データ本体への書き込みは、あちこちの場所を更新するランダムな書き込みになりがちです。一方WALはファイルの末尾にどんどん追記していくだけ(シーケンシャルな書き込み)なので、ディスクにとって得意で高速です。「重い本体の更新は後回し、軽いログだけ今すぐ確定」という役割分担が、速さと安全の両立を生みます。

fsync(物理的な書き込みの保証)

「ディスクに書いた」と思っても、実際にはOSやディスクの一時メモリに留まっているだけのことがあります。そこで使うのが fsync というシステムコールです。これは「いまOSが抱えているデータを、物理的な記憶媒体まで確実に押し出せ」という命令で、電源が落ちても消えない状態を保証します。COMMITが時々わずかに遅く感じられるのは、このWALのfsync(書き込み完了待ち)が含まれるためです。

チェックポイント(後でまとめて本体へ反映)

RAM上のダーティページは、すぐにはデータ本体へ書き戻されません。データベースはチェックポイントというタイミングで、溜まったダーティページをまとめてデータファイルへ書き出します。こうして本体への重い書き込みをまとめることで、性能を保っています。

用語場所役割
バッファプールRAMデータページをキャッシュし、ディスクアクセスを減らす
WALディスク(ログ)変更内容を先行して追記し、永続性を保証する
fsyncOS/ハードウェア境界物理媒体への書き込み完了を確実にする
チェックポイントディスク(データ本体)ダーティページをまとめてデータファイルへ反映する


クラッシュリカバリ:ログから巻き戻す

データ本体への反映が終わる前にサーバーがクラッシュしたらどうなるでしょうか。ここでWALが本領を発揮します。再起動時、データベースはWALを読み直して、記録された変更をもう一度適用(redo)します。こうして、クラッシュ直前にCOMMIT済みだった変更を確実に復元し、一貫した状態に戻せます。

クラッシュしても、flush済みのWALを再生(redo)すればCOMMIT済みの変更は失われません。

WALを再生しても「余分な変更を適用してしまわないか」と心配になるかもしれません。WALの各レコードにはトランザクションの状態(COMMITかROLLBACKか)が記録されているため、COMMITされていない途中の変更は再生されず、安全に復旧できます。


例え話:レストランの注文伝票

WALは、レストランの注文伝票に似ています。お客さんの注文(変更)が入ると、店員はまず伝票にサッと書きつけます(WALへの追記)。立派な売上台帳(データファイル)へ清書するのは、後で手が空いたときにまとめて行います(チェックポイント)。

もし清書の前に店が混乱(クラッシュ)しても、伝票さえ残っていれば注文は復元できます。伝票は走り書きで速く、台帳への清書は丁寧だけれど時間がかかる──この役割分担が、まさにWALとデータファイルの関係です。


まとめ

  • SQLの読み書きは、まずRAM上のバッファプールを経由する。遅いディスクへの直接アクセスを減らすため。
  • WALは、データ本体に反映する前に変更内容をログとして先に書き込む仕組み。
  • COMMITの正体はWALをディスクにflushすること。だから停電しても消えない(永続性)。
  • 軽いログは末尾に追記するだけなので速く、重い本体への反映はチェックポイントで後からまとめて行う。
  • クラッシュ後はWALを再生(redo)して、COMMIT済みの変更を復元する。

ロック・トランザクション・WALの3つを通して、「同時アクセスをどう守り、処理をどうまとめ、結果をどう失わずに残すか」というデータベースの土台が見えてきたはずです。まずは「変更はまずRAMとログ、本体への反映は後回し」というイメージを持っておけば十分です。


参考リソース

コメント

タイトルとURLをコピーしました