「なぜ、あのDBは突然重くなるのか?」チェックポイント調整の深淵へ
PostgreSQLを運用していると、ある日突然、特定の時間帯にレスポンスが極端に悪化する現象に直面することがあります。I/O待ちは跳ね上がり、CPU使用率は張り付くのに、クエリ自体は単純なものばかり。
その犯人の多くは「チェックポイント(Checkpoint)」です。
PostgreSQLは、データの整合性を担保するために定期的にメモリ上のダーティページをディスクに書き出します。このプロセス自体は不可欠なものですが、パラメータのチューニングを怠ると、DBのパフォーマンスを劇的に損なう「諸刃の剣」に変わります。
今回は、PostgreSQLの心臓部とも言えるチェックポイントの制御、特に`max_wal_size`(かつての`checkpoint_segments`)と`checkpoint_timeout`のバランスについて、実務的な視点で深掘りしていきます。
—
1. 「チェックポイント」がパフォーマンスを支配する理由
PostgreSQLは、更新データを即座にデータファイルへ書き込むのではなく、まずはWAL(Write-Ahead Log)に書き込み、メモリ(Shared Buffers)上のページを更新します。この「メモリ上の変更を、いつディスク上のデータファイルに反映させるか」というタイミングを決めるのがチェックポイントです。
チェックポイントが走ると何が起きるのか?
- 激しいランダムI/Oの発生: メモリ上に散らばったダーティページが一斉にディスクへフラッシュされます。これがストレージの帯域を食いつぶします。
- フルページライト(FPW)のオーバーヘッド: チェックポイント直後の最初のページ書き込みでは、ページ全体をWALに書き出す必要があり、これが書き込み負荷を増大させます。
つまり、チェックポイントの頻度が高すぎると、頻繁なI/O負荷でシステム全体が低速化し、逆に低すぎると……別のリスクが浮上します。
2. チューニングのジレンマ:書き込み負荷 vs リカバリ時間
チェックポイントの頻度は、主に以下の2つのバランスで決まります。
1. `checkpoint_timeout`: 時間によるトリガー。
2. `max_wal_size`: WALの増大量によるトリガー。
ここでの問いは、「どれだけ長くチェックポイントを先延ばしにできるか」です。
チェックポイントを遅延させるメリット
チェックポイントの間隔を広げると、データファイルへの書き込みが分散され、I/O負荷が平準化されます。また、同じページに対する複数回の更新がメモリ内で完結するため、無駄なディスク書き込みが減ります。
隠された代償:リカバリ時間
しかし、コインの裏側には「クラッシュリカバリ時間」があります。PostgreSQLが再起動する際、最後に成功したチェックポイント以降のWALをすべてリプレイする必要があります。チェックポイントの間隔が長ければ長いほど、リカバリすべきWALの量が増え、サービス復旧までの時間は伸び続けます。
「RTO(目標復旧時間)をどこまで許容できるか」というビジネス上の要件と、現在のストレージのI/O性能とのトレードオフ。ここをエンジニアとしてどう落とし込むかが、腕の見せ所です。
3. トラブルシューティング:どうやって「異常」を検知するか
PostgreSQLのログを眺めていて、以下のようなメッセージを見たことはありませんか?
> checkpoint occurs too frequently
これはPostgreSQLからの明確なSOSです。`max_wal_size`の設定値が小さすぎるため、WALの生成スピードにチェックポイントが追いつかず、無理やり回数を増やしている状態です。
この状態を放置すると、DBは常にI/Oスループットの限界で喘ぐことになります。
診断のアプローチ
私が現場で必ず確認するのは `pg_stat_bgwriter` ビューです。
SELECT checkpoints_timed, checkpoints_req, buffers_checkpoint FROM pg_stat_bgwriter;
- `checkpoints_timed`: 予定通りに完了したチェックポイント
- `checkpoints_req`: 負荷やWAL増大により「突発的」に発生したチェックポイント
もし `checkpoints_req` が `checkpoints_timed` に比べて異常に高い場合、あなたのDBは常に「綱渡り」をしています。`max_wal_size` を引き上げ、チェックポイントの発生タイミングをより「時間ベース」の予測可能なものに制御する必要があります。
4. 最後に:魔法の数字はない
よく聞かれます。「結局、最適な値はいくつですか?」と。
答えは「ワークロードによる」としか言えません。しかし、現代のNVMe SSDを積んだサーバーであれば、`checkpoint_timeout` を少し長め(例えば 15分〜30分程度)に設定し、`max_wal_size` を十分に確保して、I/Oのスパイクを抑え込むのが現代的なチューニングの定石です。
チューニングとは、DBの挙動を「飼いならす」作業です。
ログを読み、メトリクスを追い、少しずつパラメータを調整する。その過程で、データベースの呼吸が整い、I/Oの波形が滑らかになった瞬間――それが、エンジニアとして最も心地よい瞬間ではないでしょうか。
皆さんのPostgreSQLが、今日も安定して走り続けることを願っています。
コメント