WAL とチェックポイントの関係を 3 分で理解する
書き込みの応答時間が一定の間隔で延び、ディスクの書き込み量のグラフに山が等間隔で並ぶことがある。このとき、最初にチェックポイントの回数と書き出し量を確かめる。
更新は 2 回書かれる
UPDATE が完了を返した時点で、変更はデータファイルに書き込まれていない。書き込まれているのは WAL (Write-Ahead Log) だけである。
順番は次のとおり。
- 共有バッファ上のページを書き換える。この時点の変更はメモリ上だけ
- 変更内容を WAL に追記する
- コミット時に WAL を
fsyncする。fsyncが終わった変更は、クラッシュしても失われない - データファイルへの書き込みは、後のチェックポイントで行う
WAL はファイルの末尾への追記だけなので、ディスクの連続した領域に順に書ける。データファイルでは、更新されたページがファイルの中の離れた位置にあるので、書き出しはランダム書き込みになる。
WAL があるので、コミットが待つのは WAL の追記と fsync だけになり、連続した追記より時間のかかるランダム書き込みを待たずに済む。コミットの時点で書かなかったページを、まとめてデータファイルへ書き出す処理がチェックポイントである。
チェックポイントが山を作る
チェックポイントは、共有バッファ上の、データファイルにまだ書いていないページをデータファイルへ書き出す。書き出しの間、ディスクへの書き込み量が増える。グラフに等間隔で並ぶ山は、チェックポイントの書き出しである。
チェックポイントが始まる条件は 2 つである。
| 条件 | 設定 | 既定値 |
|---|---|---|
| 前回のチェックポイントから時間が経過 | checkpoint_timeout | 5 分 |
| WAL の量が上限に到達 | max_wal_size | 1 GB |
設定を変える前に、どちらの条件でチェックポイントが始まっているかを確かめる。条件によって、変える設定が違う。
select num_timed, num_requested, buffers_written, write_time, sync_timefrom 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_timefrom 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 の総量が増える。
見る順序
pg_stat_checkpointerでnum_requestedとnum_timedの比を見るnum_requestedが多いならmax_wal_sizeを増やすnum_timedがほとんどを占めても書き込み量の山が残るなら、checkpoint_completion_targetの値を確かめるcheckpoint_completion_targetを確かめても山が残るなら、山の原因はチェックポイント以外にある。VACUUM が動いた時刻を確かめる
4 つ目の VACUUM による書き込みは、VACUUM と肥大化を自分で作る で再現して確かめられる。