28.4. WALの設定

データベースの性能に影響するようなWALに関連した設定パラメータが複数あります。 本節では、その使い方を説明します。 サーバ設定パラメータの設定方法についての詳細は第18章を参照してください。

チェックポイントは、一連のトランザクションにおいて、それ以前に書き出された全ての情報でデータファイルが更新されていることが保証されている時点を指します。 チェックポイントでは、全てのダーティデータページがディスクに吐き出され、特別なチェックポイントレコードがログファイルに書き込まれます。 クラッシュした時、クラッシュからの復旧処理は最後のチェックポイント記録を見つけ、ログの中でどのレコード(これはredoレコードと呼ばれています)から復旧処理がREDOログ操作を開始すべきかを決定します。 このチェックポイント以前になされたデータの変更は、すでにディスク上にあることが分かっています。 従って、チェックポイントが行われた後、redo記録内のチェックポイント以前のログセグメントは不要となり、再利用または削除することができます (WALアーカイブが行われる場合、このログセグメントは削除もしくは再利用される前に保存されなければなりません)。

サーバのバックグラウンドライタプロセスは自動的にチェックポイントを頻繁に実行します。 checkpoint_segmentsログセグメント数に達するか、またはcheckpoint_timeout秒が経過するか、どちらかの条件が満たされるとチェックポイントが作成されます。 デフォルトの設定では、それぞれ3セグメントと300秒となっています。 また、CHECKPOINT SQLコマンドで強制的にチェックポイントを作成することもできます。

checkpoint_segmentscheckpoint_timeout、またはその両者を減少させると、チェックポイントはより頻繁に行われます。 これにより、(再処理に要する時間がより少なくなりますので)クラッシュ後の修復は高速になります。 しかし、より頻繁に行われるようになる、変更されたデータページの吐き出しにより増大するコストとバランスを考えなければなりません。 full_page_writesが設定されている(デフォルトです)場合、他に考慮しなければならない点があります。 データページの一貫性を保証するために、各チェックポイント後の最初に変更されるデータページは、そのページ全体の内容がログに保存されることになります。 このような場合、チェックポイントの間隔を少なくすることは、WALログへの出力を増加させ、間隔を短くする目的の一部を無意味にします。 また、確実により多くのディスクI/Oが発生します。

チェックポイントはかなり高価なものです。 1番の理由は、この処理は現時点の全てのダーティバッファを書き出す必要があること、2番目の理由は、上記のようにその後に余計なWALの書き込みが発生することです。 そのため、チェックポイント用のパラメータを高くし、チェックポイントがあまりにも頻発することがないようにすることを勧めます。 簡単なチェックポイント用のパラメータの健全性検査として、checkpoint_warningパラメータを設定することができます。 チェックポイントの発生間隔がcheckpoint_warning秒未満の場合、checkpoint_segmentsの増加を勧めるメッセージがサーバのログに出力されます。 このメッセージが稀に現れたとしても問題にはなりませんが、頻出するようであれば、チェックポイントの制御パラメータを増加させるべきです。 checkpoint_segmentsを十分高く設定していないと、大規模なCOPY転送などのまとまった操作でこうした警告が多く発生するかもしれません。

ページ書きだしの集中による入出力システムの溢れを防ぐために、チェックポイント期間のダーティバッファの書きだしは一定の期間に分散されます。 この期間はcheckpoint_completion_targetにより制御され、チェックポイント感覚の割合として指定されます。 I/Oの割合は、チェックポイントの起動時からcheckpoint_segments WALセグメントが消費された時、あるいは、指定したcheckpoint_timeout秒が経過した時、このどちらかが発生するとすぐに、にチェックポイントが完了するように調整されます。 デフォルトの0.5という値では、PostgreSQLは、次のチェックポイントが始まるまでのおよそ半分の時間で各チェックポイントが完了するものと想定できることになります。 通常の操作においてほぼ最大のI/Oスループットに近いようなシステムでは、チェックポイントにおけるI/O負荷を減らすためにcheckpoint_completion_targetを増やすことを勧めます。 この欠点は、延長されたチェックポイントがリカバリ時に影響をあたえることです。 リカバリ時に使用できるように、より多くのWALセグメントを保持する必要があるためです。 checkpoint_completion_targetを最大の1.0に設定することもできますが、より低く抑えること(おそらく最大で0.9)が最善です。 チェックポイントには、ダーティバッファを書き出す以外の活動も含まれているからです。 1.0という設定は、ある時点でチェックポイントが完了しなくなるという結果に陥ります。 これは必要なWALセグメント数が想定以上に変動することになり、性能の劣化が発生することになります。

There will always be at least one WAL segment file, and will normally not be more than (2 + checkpoint_completion_target) * checkpoint_segments + 1 files. Each segment file is normally 16 MB (though this size can be altered when building the server). You can use this to estimate space requirements for WAL. Ordinarily, when old log segment files are no longer needed, they are recycled (renamed to become the next segments in the numbered sequence). If, due to a short-term peak of log output rate, there are more than 3 * checkpoint_segments + 1 segment files, the unneeded segment files will be deleted instead of recycled until the system gets back under this limit. --> WALセグメントファイルは常に少なくとも1つあり、また、通常は(2 + checkpoint_completion_targetcheckpoint_segments + 1より多くはありません。 各セグメントファイルは通常16メガバイト(このサイズはサーバのコンパイル時に変更可能)です。 このことから、WALで必要とされる領域を推定できます。 普通、古いセグメントファイルが不要になった時、それらは再利用(将来順番に使われるセグメントとなるように名前が変更)されます。 短時間のログ出力のピークのためにセグメントファイル数が3×checkpoint_segments + 1を超えた場合、システムは、この上限以下になるまで、不要になったセグメントファイルを再利用せずに、削除します。

よく使われる2つの内部用WAL関数があります。 LogInsertLogFlushです。 LogInsertは共有メモリ上のWALバッファに新しいレコードを挿入します。 新しいレコードを挿入する余地がない時は、LogInsertは、満杯になったWALバッファを書き込み(カーネルキャッシュに移動)しなければいけません。 これは望ましいことではありません。 なぜなら、データベースへの低レベルの変更(例えば行の挿入)の度にLogInsertが呼ばれますが、そのような場合には変更を受けたページに対して排他ロックがかかっており、それゆえこの操作は可能な限り高速に実行されなければなりません。 さらに悪いことには、WALバッファへの書き込みの際に、さらに時間がかかる、強制的な新しいログセグメントの生成が必要となるかもしれません。 通常、WALの書き込み、吐き出しはLogFlush要求で実施されます。 これはたいていの場合、トランザクションコミットの際に永続的な記憶領域にトランザクションレコードが吐き出されることを保証するために行われます。 ログ出力が大量に行われるシステムでは、LogInsertによって必要となる書き込みを防ぐほどにはLogFlush要求が頻繁に起こらないかもしれません。 そういうシステムでは、wal_buffers設定パラメータを変更してWALバッファの数を増やしてください。 デフォルトのWALバッファの数は8です。 この数を増やすと共有メモリの使用量に影響があります。 full_page_writesが設定され、かつ、システムが高負荷状態である場合、この値を高くすることで、各チェックポイントの直後の応答時間を滑らかにすることができます。

commit_delayパラメータは、LogInsertがコミットレコードをログに書き込んでからLogFlushが行われるまでの間にサーバプロセスが何マイクロ秒休止するかを定義します。 この遅延により、他のサーバプロセスがコミットレコードをログに書き込んだ後、それら全てのログレコードを1回のログ同期で吐き出すことができます。 fsyncが無効な場合や、commit_siblingsよりも少ない数のセッションしか現在アクティブなトランザクションに存在しない場合は、休止しません。 この処理により、すぐにコミットしそうなセッションがない時でも休止してしまうことを防ぐことができます。 たいていのプラットフォームでは、休止の最小単位は10ミリ秒であることに注意してください。 ですから、1マイクロ秒から10,000マイクロ秒までの間での0以外のcommit_delayの設定は、同じ効果になるでしょう。 これらのパラメータの適切な値はまだ明確ではありません。 実験が必要です。

wal_sync_methodパラメータはPostgreSQLがカーネルに対してWAL更新のディスクへの書き込みを要求する方法を決定します。 どういう設定でも信頼性は同じはずですが、プラットフォームによってどれが一番速いのかがまったく違います。 ちなみに、このパラメータはfsyncが無効になっている場合は役に立ちません。

wal_debug設定パラメータを有効にすることで、LogInsertLogFlushというWAL呼び出しは毎回サーバログにログが残ります (このパラメータをサポートするようにPostgreSQLをコンパイルする必要があります)。 将来このオプションはより一般的な機構に置き換わる可能性があります。

アダルトレンタルサーバー