本文へスキップ

記事postgres

WAL とチェックポイントの関係を 3 分で掴む

書き込みが定期的に遅くなる。 グラフに山が等間隔で並ぶ。

このときまず疑うのはチェックポイントです。

更新は 2 回書かれる

UPDATE が返ってきた時点で、変更はデータファイルに届いていません。 届いているのは WAL (Write-Ahead Log) だけです。

順番はこうです。

  1. 共有バッファ上のページを書き換える。この時点ではメモリだけ
  2. 変更内容を WAL に追記する
  3. コミット時に WAL を fsync する。ここまで来たらもう失われない
  4. データファイルへの反映は後回し

WAL は追記だけなので、円盤の同じ場所に順に書けます。 データファイルはページが散らばっているので、書けばランダム書き込みになります。

この非対称が WAL の理由です。 遅い書き込みを後回しにして、速い書き込みだけを同期の待ちに入れます。

後回しにしたものを、まとめて片付けるのがチェックポイントです。

チェックポイントが山を作る

チェックポイントは、共有バッファ上の未反映のページをデータファイルへ流します。 このとき I/O が跳ねます。等間隔の山の正体はこれです。

引き金は 2 つです。

引き金設定既定
時間が来たcheckpoint_timeout5 分
WAL が溜まったmax_wal_size1 GB

どちらで起きているかを先に確かめてください。 打ち手が変わります。

select checkpoints_timed, checkpoints_req,
buffers_written, write_time, sync_time
from pg_stat_checkpointer;

checkpoints_req (WAL 量による強制) が checkpoints_timed (時間による定期) に対して多いなら、max_wal_size が書き込み量に対して小さすぎます。

Postgres 17 より前は pg_stat_bgwriter に同じ列があります。

間隔を延ばすと山が高くなる

checkpoint_timeout を延ばせば回数は減ります。 そのぶん 1 回で流す量が増えます。

総量は変わりません。 山が低く広くなるか、高く狭くなるかが変わるだけです。

延ばす代償は復旧時間です。 クラッシュしたとき、最後のチェックポイント以降の WAL を全部読み直します。間隔が長いほど、読み直す量が増えます。

均すための設定が checkpoint_completion_target です。 既定の 0.9 は「次のチェックポイントまでの 90% の時間をかけて、少しずつ流す」という意味です。

まとめると、つまみは 3 つです。

したいこと触る場所代償
山の回数を減らすcheckpoint_timeout を延ばす復旧時間が延びる
WAL 起因の強制を止めるmax_wal_size を増やす円盤を食う
山をならすcheckpoint_completion_target を 1.0 へ寄せるほぼ無い。既定で 0.9

full page writes

チェックポイント直後だけ WAL の量が跳ねることがあります。

Postgres は、チェックポイント後に最初に変更されたページを、差分ではなくページ丸ごと WAL に書きます。 書き込みの途中で電源が落ちてページが半分だけ書かれる (torn page) 事故から復旧するためです。

このため、チェックポイントの間隔を短くすると WAL の総量そのものが増えます。 「山を低くするために間隔を縮める」は、WAL 量では逆に働きます。

見る順序

  1. pg_stat_checkpointercheckpoints_reqcheckpoints_timed の比を見る
  2. req が多いなら max_wal_size を増やす
  3. timed ばかりで山が高いなら checkpoint_completion_target を確かめる
  4. それでも高いなら、山の正体はチェックポイントではない。VACUUM を疑う

4 つ目は VACUUM と肥大化を自分で作る で確かめられます。

関連

2026.08.30VACUUM と肥大化を自分で作るハンズオン