25.3. WALの設定

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

チェックポイントは、一連のトランザクションにおいて、それ以前にログされたすべての情報でデータファイルが更新されていることが保証されている時点を指します。 チェックポイントでは、すべての変更されたデータページがディスクに吐き出され、特別なチェックポイントレコードがログファイルに書き込まれます。 その結果、クラッシュが発生した際に、ログの中でどのレコード(これはredoレコードと呼ばれています)から復旧処理がREDOログ操作を開始すべきかを知ることができます。 なぜなら、redoレコード以前にデータファイルに対して行われた変更はすでにディスク上に記録済みだからです。 チェックポイントが実施された後、redoレコード以前のすべてのログセグメントは不要となり、削除、または再利用することができます。 (WAL ベースの BAR が実装された時、このログセグメントは削除もしくは再利用される前に保存されることになります。)

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

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

16M バイトのセグメントファイル数は少なくとも 1 つあり、また、通常は 2 * checkpoint_segments + 1 より多くはありません。 このことから、WAL で必要とされる領域を推定できます。 普通、古いセグメントファイルが不要になった時、それらは再利用(将来順番に使われるセグメントとなるように名前が変更)されます。 短時間のログ出力のピークのためにセグメントファイル数が 2 * checkpoint_segments + 1 を越えた場合、システムがこの上限以下になるまで、不要になったセグメントファイルを再利用するのではなく、削除します。

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

チェックポイントは、オペレーティングシステムのsync()コールを使用して、変更されたカーネルバッファをすべてディスクに強制しますので、かなり高コストなものです。 忙しいサーバでは、チェックポイントセグメントファイルはすぐに一杯になってしまい、高コストなチェックポイント処理が行なわれることになるかもしれません。 こうした強制的なチェックポイントがcheckpoint_warning秒より頻繁に発生するのであれば、checkpoint_segmentsの増加を勧めるメッセージがサーバログに出力されるようになります。

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

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

WAL_DEBUGパラメータを0以外の値にすることで、LogInsertLogFlushWALにおける呼び出しがそれぞれサーバログに出力されるようになります。 いまのところ、値による違いはありません。 将来はもっと汎用的な方法に置き換えられるかもしれません。