-
2023-08-01 PostgreSQLPostgreSQL パラメータ
出力されたWALファイルのサイズとチェックポイントが発生するタイミング
WAL ファイルの出力を契機としたチェックポイント
今回は WAL ファイルの出力を契機として行われるチェックポイントについて、調べた結果をお伝えします。
WAL ファイルの出力を契機としたチェックポイントとは、更新処理などによって発生した WAL ファイルの合計サイズが閾値を超えたために行われる、緊急的なチェックポイントのことです。
ちなみに PostgreSQL のログ上では
2023-06-21 10:46:09.135 JST [30084] LOG: checkpoint starting: wal
という形で出力されます。
緊急的なチェックポイントなので、WAL ファイルの出力を契機としたチェックポイントは可能な限り発生させず、時間経過を契機としたチェックポイントが繰り返し行われる状況が理想です。
公式ドキュメントでの説明
公式ドキュメント( https://www.postgresql.jp/document/current/html/wal-configuration.html )の説明を引用します。
サーバのチェックポインタプロセスは、自動的にチェックポイントを時々実行します。 checkpoint_timeout秒が経過するか、またはmax_wal_sizeに達するか、どちらかの条件が最初に満たされるとチェックポイントが開始されます。
翻訳元の英文は下記となります。
A checkpoint is begun every
seconds, or if is about to be exceeded, whichever comes first.
この文章だけ読むと、「WAL ファイルの出力サイズが max_wal_size 付近になったらチェックポイント処理が行われるのか」と理解してしまいそうになるのですが、実際にはそうでありません。
WAL ファイルの出力サイズを契機としたチェックポイント処理には、max_wal_size と checkpoint_completion_target が関係しています。
ソースコードの確認
ここで内部的なお話となってしまうのですが、WAL ファイルの出力サイズを契機としたチェックポイント処理を行うかどうかは xlog.c の XLogCheckpointNeeded 関数で決定されます。
ちなみに xlog.c は src/backend/access/transam にあります。
XLogCheckpointNeeded 関数
XLogCheckpointNeeded 関数は数行のコードですので、下記に引用します。
バージョンは PostgreSQL 15.3 です。
bool
XLogCheckpointNeeded(XLogSegNo new_segno)
{
XLogSegNo old_segno;
XLByteToSeg(RedoRecPtr, old_segno, wal_segment_size);
if (new_segno >= old_segno + (uint64) (CheckPointSegments - 1))
return true;
return false;
}
8 行目の if 文が、チェックポイント処理を行うかどうかの判定式となります。
ざっくりとした説明になりますが
- new_segno は、書き込み先(コメントでは「いっぱいになったばかりの」と表現されています)の WAL ファイル
- old_segno は、最後にチェックポイントされた時点の WAL ファイル
- CheckPointSegments は、チェックポイントを発生させる WAL ファイルの数の閾値
という内容になっています。
つまりは、CheckPointSegments の値がチェックポイントを発生させるかどうかに関係するのですが、この値は xlog.c の CalculateCheckpointSegments 関数で算出されます。
CalculateCheckpointSegments 関数
CalculateCheckpointSegments 関数も(コメントを除くと)数行のコードですので、下記に引用します。
static void
CalculateCheckpointSegments(void)
{
double target;
target = (double) ConvertToXSegs(max_wal_size_mb, wal_segment_size) /
(1.0 + CheckPointCompletionTarget);
/* round down */
CheckPointSegments = (int) target;
if (CheckPointSegments < 1)
CheckPointSegments = 1;
}
こちらもざっくりとした説明になりますが、max_wal_size と checkpoint_complation_target からチェックポイントを発生させるかどうかの閾値を算出しています。
max_wal_size が 1024 MB、WAL ファイルのサイズが 16 MB で、checkpoint_completion_target が 0.9 の場合、
(1024 / 16 ) / (1.0 + 0.9) = 33.6842105
となります。計算結果は切り捨てられるので最終的な計算結果は 33 となります。
この 33 は WAL ファイルの数を表しますので、サイズに変換する( 16 MB をかける)と 528 MB となります。
max_wal_size が 1024 MB、WAL ファイルのサイズが 16 MB で、checkpoint_completion_target が 0.5 の場合、
(1024 / 16 ) / (1.0 + 0.5) = 42.6666667
となり、小数を切り捨てると 42 となります。こちらもサイズに変換すると 672 MB となります。
結局のところ、どれぐらいの WAL ファイルが作成されたらチェックポイントが行われるのか
ということですが、checkpoint_completion_target が 1 に近づくにつれ、チェックポイントが発生しやすくなるということになります。
具体的には checkpoint_completion_target が 0.9 の場合、max_wal_size の 51 % 前後の WAL ファイルが作成された場合にチェックポイントが行われます。
checkpoint_completion_target が 0.5 の場合、max_wal_size の 65 % 前後の WAL ファイルが作成された場合にチェックポイントが行われます。
まとめ
今回の記事では、WAL ファイルの出力サイズを契機としたチェックポイント処理の実行タイミングが、max_wal_size と checkpoint_completion_target の値によって変動することを説明いたしました。
公式ドキュメントの情報は非常に有益ですが、それと合わせてソースコードを確認すると、より PostgreSQL を理解できるようになります。
とは言ってもソースコードは量が膨大な上、複雑ですので、ソースコードのコメントや各ディレクトリに配置されている README ファイルを読むことから始めるのがお勧めです。
※ 計算式が一部間違っていましたので修正いたしました(2024/09/20)