PostgreSQLの心臓部、WAL(Write-Ahead Log)を徹底解説!~データ保全と可用性を支える縁の下の力持ち~
やあ、みんな!今日はPostgreSQLの超重要機能であるWAL(Write-Ahead Log)について、現場の先輩エンジニアとして、みんなに分かりやすく、そして実践的に解説していこうと思う。DBエンジニアなら避けては通れない道だから、しっかりマスターしておこうね!
WALって、そもそも何? なんで必要なの?
まず、WALって言葉を聞いたことあるかな? 「Write-Ahead Log」の略で、直訳すると「書き込み先行ログ」だ。名前の通り、データファイルへの変更を実際に書き込む前に、その変更内容をログファイルに記録しておく、という仕組みなんだ。
「え、なんでわざわざ二度手間? 直接データファイルに書いちゃえば早いじゃん!」って思った? そうだよね、最初はみんなそう思うんだ(笑)。でも、これにはちゃんとした理由がある。PostgreSQLがACID特性、特に永続性(Durability)を保証するために、そしてクラッシュリカバリを可能にするために、このWALがめちゃくちゃ重要な役割を果たしているんだ。
ACID特性とWALの関係
ACID特性って覚えているかな?
- Atomicity(原子性): トランザクションは完全に実行されるか、全く実行されないかのどちらか。
- Consistency(一貫性): トランザクションの前後で、データベースは常に一貫した状態である。
- Isolation(独立性): 複数のトランザクションが同時に実行されても、互いに干渉しない。
- Durability(永続性): コミットされたトランザクションの結果は、システム障害が発生しても失われない。
この中で、WALが直接的に保証しているのが「永続性」なんだ。
考えてみてほしい。あるトランザクションでデータを更新して `COMMIT` したとする。でも、その直後にサーバーがクラッシュしてしまったらどうなる? データファイルへの変更がディスクに書き込まれる前にクラッシュしたら、その変更は失われてしまうかもしれない。これは「永続性」が保たれていない状態だ。
WALがあるおかげで、コミットが完了したトランザクションの変更内容は、必ずWALファイルに書き込まれる。そして、そのWALファイルがディスクに安全に書き込まれた後に、データファイルへの変更が非同期で書き込まれていく。だから、万が一クラッシュしても、WALファイルを見れば「コミットされたはずの変更」を復元できるんだ。これがWALの真骨頂!
WALの仕組みをもう少し深く見てみよう
WALは、具体的にどんな風に動いているんだろう? PostgreSQLの内部では、いくつかのコンポーネントが連携してWALを処理しているんだ。
1. WALライタープロセス (Background Writer Process)
これはWALの重要なプレーヤーの一つ。データページがメモリ上で変更されると、その変更内容はWALバッファに記録される。WALライタープロセスは、このWALバッファの内容を定期的にディスク上のWALセグメントファイルに書き込む役割を担っている。
2. WALバッファ
メモリ上に確保されている領域で、WALレコード(変更履歴)を一時的に保持する。データファイルへの書き込みよりもWALへの書き込みの方が高速なので、ここでいったんまとめてディスクに書き出すことで、パフォーマンスを向上させているんだ。
3. WALセグメントファイル
WALレコードが最終的に書き込まれるディスク上のファイル。PostgreSQLはWALファイルを一定のサイズ(`wal_segment_size` で設定、デフォルトは16MB)ごとに分割して管理している。これらのファイルは `pg_wal` ディレクトリ(PostgreSQL 9.5以前は `pg_xlog`)に保存される。
WALレコードって、どんな情報が入ってるの?
WALレコードには、データ変更を元に戻したり、再現したりするために必要な情報が含まれている。例えば、
- どのテーブルの、どの行が、どのように変更されたか
- SQL文そのもの(`wal_level = minimal` 以外の場合)
などだ。このWALレコードのおかげで、クラッシュリカバリやレプリケーションが実現できるんだ。
WALの重要な設定パラメータ
WALの挙動を調整するための設定パラメータはいくつかある。現場でよく使うものをいくつか紹介しよう。
`wal_level`
WALに記録される情報量のレベルを設定する。
- `minimal`: 最低限の情報のみを記録。クラッシュリカバリには必要だが、レプリケーションやPITR(Point-in-Time Recovery)には不十分。
- `replica`: `minimal` に加えて、レプリケーションに必要な情報も記録。WALアーカイブやストリーミングレプリケーションのベースとなる。
- `logical`: `replica` に加えて、論理デコーディングに必要な情報も記録。論理レプリケーションなどで使用。
通常、レプリケーションやPITRを利用するなら `replica` 以上に設定するのが一般的だ。
`wal_sync_method`
WALバッファの内容をディスクに書き込む際の同期方法を指定する。OSの機能に依存する部分が大きい。`fsync` や `open_datasync` などがある。デフォルトはOSが推奨するものが使われることが多いが、パフォーマンスや信頼性に関わるので、ストレージの特性に合わせて調整することもある。
`wal_writer_delay`
WALライタープロセスが、WALバッファをディスクに書き込むまでの待機時間を指定する。デフォルトは200ms。この値を短くするとWALへの書き込み頻度が増え、クラッシュリカバリの速度は上がる可能性があるが、I/O負荷は増加する。
`wal_buffers`
WALバッファのサイズを指定する。デフォルトは通常 `shared_buffers` の1/32だが、最大でも16MB。WALの書き込みパフォーマンスに影響する。
`max_wal_size` (PostgreSQL 9.5以降) / `checkpoint_segments` (それ以前)
チェックポイントの間隔を制御するパラメータ。チェックポイントとは、WALバッファの内容をすべてデータファイルに書き込み、WALファイル上に「チェックポイント」という印を付ける処理のこと。これにより、クラッシュリカバリの際にWALを最初から全て読み込む必要がなくなり、リカバリ時間が短縮される。
- `max_wal_size`: checkpointが発生するまでに蓄積されるWALの最大サイズを指定する。
- `checkpoint_segments`: checkpointの間隔をWALセグメントファイル数で指定する。
`max_wal_size` の設定が重要で、大きすぎるとクラッシュリカバリに時間がかかる可能性があり、小さすぎるとチェックポイントが頻繁に発生してI/O負荷が増える。ストレージの性能やリカバリ時間に求められる要件に応じて調整しよう。
実践!WALの活用例 ~PITRとレプリケーション~
WALの仕組みを理解したところで、次にその具体的な活用例を見ていこう。現場で最もよく使われるのは、この二つだろう。
1. PITR (Point-in-Time Recovery)
PITRは、データベースを過去のある時点の状態に復旧させる機能だ。WALの恩恵を最も直接的に感じられる機能の一つと言える。
仕組み:
1. 定期的にデータベースのフルバックアップを取得する。
2. バックアップ取得後もWALは継続して生成され続ける。
3. 失われたり破損したりした場合は、最新のフルバックアップをリストアする。
4. その後、バックアップ取得後のWALファイルを順に適用していく。
5. 目的の復旧時点までWALを適用することで、その時点の状態に復旧できる。
設定:
PITRを行うには、まず `wal_level = replica` 以上に設定し、WALアーカイブを設定する必要がある。WALアーカイブとは、WALセグメントファイルが一杯になったときに、それを別の場所にコピーしておく処理のことだ。
.conf
wal_level = replica
archive_mode = on
archive_command = ‘cp %p /path/to/wal_archive/%f’ # 例: cpコマンドでWALファイルをアーカイブディレクトリにコピー
`archive_command` は、WALファイルをどのようにアーカイブするかを指定するコマンドだ。上記は単純に `cp` コマンドでコピーする例だが、実際には `rsync` を使ったり、専用のバックアップツールを使ったりすることが多い。
復旧手順(概念):
1. 最新のフルバックアップをリストアする。
2. `recovery.conf` (PostgreSQL 12以降は `postgresql.conf` 内の `restore_command` など) を用意して、WALファイルをどのように取得するか指定する。
3. `recovery.conf` に `restore_command` を設定する。
restore_command = ‘cp /path/to/wal_archive/%f %p’
4. `recovery.conf` に `recovery_target_time = ‘YYYY-MM-DD HH:MM:SS’` のように復旧したい日時を指定する。
5. PostgreSQLを起動する。
PostgreSQLは `recovery.conf` を見て、指定された時点までWALを適用して復旧してくれる。
2. レプリケーション (ストリーミングレプリケーション)
ストリーミングレプリケーションは、プライマリサーバーのWALストリームをスタンバイサーバーにリアルタイムで転送し、スタンバイサーバーでそれを再生することで、プライマリサーバーと同期を保つ仕組みだ。
仕組み:
1. プライマリサーバーはWALレコードを生成し、WALバッファに書き込む。
2. WALライタープロセスがWALバッファの内容をWALセグメントファイルに書き出す。
3. WAL送信プロセス(WAL sender process)が、WALセグメントファイルの内容をスタンバイサーバーにストリーミングする。
4. スタンバイサーバーのWAL受信プロセス(WAL receiver process)は、受け取ったWALストリームをローカルのWALファイルに書き込む。
5. スタンバイサーバーのロジカルリプロダクションプロセス(またはWALリプレイヤー)が、受信したWALを再生し、自身のデータファイルを更新していく。
設定:
プライマリサーバー側でWALレベルを `replica` 以上にし、レプリケーションに必要な設定を行う。
.conf (プライマリ)
wal_level = replica
max_wal_senders = 5 # 同時に送信できるWALの数
wal_keep_size = 1GB # スタンバイが接続できない場合に保持しておくWALのサイズ (PG13以降)
スタンバイサーバー側で `recovery.conf` (または `postgresql.conf`) を設定し、プライマリサーバーへの接続情報を指定する。
.conf (スタンバイ)
hot_standby = on # 読み取り専用クエリを受け付けるか
primary_conninfo = ‘host=primary_host port=5432 user=repl_user password=repl_password’ # プライマリへの接続情報
この設定により、プライマリサーバーの変更がリアルタイムでスタンバイサーバーに反映される。これにより、高可用性や読み取り負荷分散を実現できるんだ。
WALファイルが貯まりすぎて困った! ~WALの管理と注意点~
WALは非常に便利だけど、管理を怠るとディスク容量を圧迫したり、リカバリに時間がかかりすぎたりといった問題を引き起こすこともある。
WALファイルの削除とアーカイブ
WALセグメントファイルは、PostgreSQLが起動している間は基本的に削除されない。なぜなら、いつクラッシュリカバリやレプリケーションで必要になるか分からないからだ。
- WALアーカイブ: `archive_mode` を `on` にしてWALアーカイブを設定している場合、WALセグメントファイルが一杯になると、`archive_command` で指定したコマンドが実行されて、そのファイルはアーカイブ先にコピーされる。コピーが成功したら、PostgreSQLは不要になったWALセグメントファイルを削除してくれる(ただし、`wal_keep_size` の設定など、削除されない条件もある)。
- WALアーカイブを設定していない場合: WALアーカイブを設定していないと、WALファイルはどんどん溜まっていく一方になる。ディスク容量が枯渇する前に、手動で不要なWALファイルを削除したり、PostgreSQLを再起動してWALの連番をリセットしたりする必要が出てくる。ただし、この方法はリスクが高いので、基本的にはWALアーカイブを設定することを強く推奨する。
チェックポイントの頻度
前述した `max_wal_size` や `checkpoint_segments` の設定が適切でないと、チェックポイントが頻繁に発生してI/O負荷が高まったり、逆にチェックポイントが少なすぎてクラッシュリカバリに時間がかかりすぎたりする。ストレージの性能やリカバリ時間に求められるRTO(目標復旧時間)を考慮して、適切な値にチューニングしよう。
WALレベルの変更
`wal_level` を `minimal` から `replica` や `logical` に変更する場合は、PostgreSQLの再起動が必要になる。また、`replica` から `minimal` に戻す場合も同様だ。変更する際は、システムへの影響を十分に考慮して、計画的に実施するようにしよう。
まとめ:WALはPostgreSQLの信頼性を支える要!
どうだったかな? WALは、PostgreSQLがACID特性、特に永続性を保証し、クラッシュリカバリやレプリケーションを実現するための、まさに縁の下の力持ちなんだ。
- データ変更を書き込む前にログに記録するのがWAL。
- 永続性とクラッシュリカバリの要。
- PITRやストリーミングレプリケーションの基盤。
- `wal_level` や `max_wal_size` などの設定を理解し、適切にチューニングすることが重要。
- WALアーカイブを設定して、ディスク容量の圧迫やリカバリ時間の増大を防ごう。
WALの仕組みをしっかり理解しておくことは、データベースの安定稼働、障害発生時の迅速な復旧、そして高度な機能(レプリケーションなど)の活用に不可欠だ。今日話した内容を参考に、ぜひみんなの環境でもWALの恩恵を最大限に引き出してほしい。
何か分からないことがあれば、いつでも聞いてくれよな! それじゃ、また!
コメント