本文へスキップ

記事postgres

WAL とチェックポイントの関係を 3 分で理解する

書き込みの応答時間が一定の間隔で延び、ディスクの書き込み量のグラフに山が等間隔で並ぶことがある。このとき、最初にチェックポイントの回数と書き出し量を確かめる。

更新は 2 回書かれる

UPDATE が完了を返した時点で、変更はデータファイルに書き込まれていない。書き込まれているのは WAL (Write-Ahead Log) だけである。

順番は次のとおり。

  1. 共有バッファ上のページを書き換える。この時点の変更はメモリ上だけ
  2. 変更内容を WAL に追記する
  3. コミット時に WAL を fsync する。fsync が終わった変更は、クラッシュしても失われない
  4. データファイルへの書き込みは、後のチェックポイントで行う

WAL はファイルの末尾への追記だけなので、ディスクの連続した領域に順に書ける。データファイルでは、更新されたページがファイルの中の離れた位置にあるので、書き出しはランダム書き込みになる。

WAL があるので、コミットが待つのは WAL の追記と fsync だけになり、連続した追記より時間のかかるランダム書き込みを待たずに済む。コミットの時点で書かなかったページを、まとめてデータファイルへ書き出す処理がチェックポイントである。

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

チェックポイントは、共有バッファ上の、データファイルにまだ書いていないページをデータファイルへ書き出す。書き出しの間、ディスクへの書き込み量が増える。グラフに等間隔で並ぶ山は、チェックポイントの書き出しである。

チェックポイントが始まる条件は 2 つである。

条件設定既定値
前回のチェックポイントから時間が経過checkpoint_timeout5 分
WAL の量が上限に到達max_wal_size1 GB

設定を変える前に、どちらの条件でチェックポイントが始まっているかを確かめる。条件によって、変える設定が違う。

select num_timed, num_requested,
buffers_written, write_time, sync_time
from pg_stat_checkpointer;

num_timed は checkpoint_timeout で始まった回数、num_requested は max_wal_size に達したときなど、要求で始まった回数である。num_requested が num_timed より多いなら、checkpoint_timeout の時間が経つ前に WAL が max_wal_size に達している。

Postgres 16 までは、同じ値を pg_stat_bgwriter から別の列の名前で読む。

select checkpoints_timed, checkpoints_req,
buffers_checkpoint, checkpoint_write_time, checkpoint_sync_time
from pg_stat_bgwriter;

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

checkpoint_timeout を延ばせば、チェックポイントの回数は減る。その代わり、1 回で書き出すページ数が増え、書き込み量の山が高くなる。同じページを何度更新しても、データファイルへ書き出すのはチェックポイントごとに 1 回である。そのため、書き出すページの総数は、間隔を延ばしても増えない。

延ばすと、クラッシュからの復旧にかかる時間が延びる。クラッシュの後の起動で、Postgres は最後のチェックポイント以降の WAL をすべて読み直す。チェックポイントの間隔が長いほど、読み直す WAL の量が増える。

1 回のチェックポイントの書き出しを、時間をかけて分散させる設定が checkpoint_completion_target である。既定値の 0.9 は「次のチェックポイントまでの時間の 90% をかけて、少しずつ書き出す」という意味である。

目的ごとの、変える設定と代償は次のとおり。

したいこと変える設定代償
チェックポイントの回数を減らすcheckpoint_timeout を延ばす復旧時間が延びる
要求によるチェックポイントを減らすmax_wal_size を増やすディスクの使用量が増える
書き込み量の山を低くするcheckpoint_completion_target を既定値の 0.9 にする0.9 より上げると、書き出し以外の処理の時間が残らず、次のチェックポイントまでに終わらないことがある

full page writes

チェックポイントの直後には、ほかの時間より WAL の書き込み量が増えることがある。

Postgres は、チェックポイントの後に最初に変更されたページを、差分ではなくページ丸ごと WAL に書く。書き込みの途中で電源が落ち、ページの一部だけがディスクに書かれた状態 (torn page) からも、WAL に残したページ全体で復旧するためである。

このため、1 回の書き出し量を減らそうとチェックポイントの間隔を縮めると、ページ丸ごとの書き込みの回数が増え、WAL の総量が増える。

見る順序

  1. pg_stat_checkpointer で num_requested と num_timed の比を見る
  2. num_requested が多いなら max_wal_size を増やす
  3. num_timed がほとんどを占めても書き込み量の山が残るなら、checkpoint_completion_target の値を確かめる
  4. checkpoint_completion_target を確かめても山が残るなら、山の原因はチェックポイント以外にある。VACUUM が動いた時刻を確かめる

4 つ目の VACUUM による書き込みは、VACUUM と肥大化を自分で作る で再現して確かめられる。

関連

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