Postgres のログ先行書き込みの使用法
最終更新日 2022年12月12日(月)
Table of Contents
Heroku Postgres は、継続的保護の一環としてログ先行書き込みを使用します。また、信頼性の高い外部ストレージへの継続的 WAL アーカイブを使用します。
この記事では、WAL がどのように使用されるか、WAL の生成速度が WAL のアーカイブ速度を上回る場合に起こりうる問題、WAL の生成過多を防ぐための戦略について説明します。
ログ先行書き込みとは
ログ先行書き込み (WAL) は、Postgres の耐久性とデータ整合性の保証を実現するためのコア部分です。すべての変更は、最初にこの追記専用のログに書き込まれた後、ディスク上のデータファイルに伝播されます。
これには、一時テーブルやログに記録されないテーブルなどの例外があります。これらのテーブルでは、変更は最初に WAL に書き込まれないため、クラッシュに対して安全ではなく、複製もできません。
Heroku Postgres では、WAL ファイルをローカルディスクにまず永続化します。WAL の容量が完全にいっぱいになると、データベースはシャットダウンし、データが失われるリスクが生じます。
一般的に言えば、Postgres は (INSERT
、UPDATE
などの) 書き込み操作を実行するときに WAL を生成します。WAL の生成速度がディスクへの WAL アーカイブ速度を上回ると、WAL の容量が減少します。また、データベースの負荷が高くなると、アーカイブ処理のスループットが低下します。
できること
WAL 容量の監視
Heroku Postgres のメトリクスのログでは、sample#wal-percentage-used
メトリクスが Heroku から出力されます。正常なデータベースでは、このメトリクスは 0.75
以下です。0.75
を上回ると、Heroku によって Heroku Postgres インスタンスへの接続が自動的に制限されます。
接続が制限されると、次の構造の追加のログ行が Heroku により出力されます。
source=DATABASE_URL addon=postgresql-rugged-12345 sample#wal-percentage-used=0.88 sample#max-connections=120 message=Database WAL usage is too high, throttling connections.
監視機能を提供する一部の Heroku アドオンパートナーは、sample#wal-percentage-used
メトリクスのグラフ化とそれに基づくアラート、および追加のログ行の外観を支援できます。
WAL 生成の削減
WAL の生成は、Heroku Postgres への書き込み速度を制限することによって削減できます。このセクションではいくつかの戦略を示します。どの戦略が用いられるかはワークロードによって異なるため、Heroku ではすべての戦略の詳細な説明は提供できません。
Postgres のパーティション分割を使用して大きなテーブルを分割する
大きなテーブルがあるデータベースでは、Postgres ネイティブのパーティション分割 を使用して大きなテーブルを小さなテーブルに分割すると効果が大きい可能性があります。このプロセスによって、テーブルのサブセットではなくパーティション全体を操作してデータを削除およびアーカイブできるようになり、WAL の生成量が大きく減少します。
データの一括削除に DROP
と TRUNCATE
を使用する
“過去 N 日/週/月” などの範囲で保持されて定期的に削除されるデータセットでは、テーブルのパーティション分割と時間範囲別のパーティションを使用します。その後、Postgres の DROP
または TRUNCATE
を使用してパーティションを削除します。DROP TABLE
と TRUNCATE
では WAL の生成量が少なく、削除されるデータの量に比例しません。一方、DELETE
では、削除されるデータの量に比例した量の WAL が生成されます。
同じデータの削除とその後の再挿入を行わない
定期的に WAL 容量を使い果たす Heroku Postgres の顧客にみられるパターンは、データのかなりの部分を削除した後、同じデータを外部ソースから再インポートすることを繰り返すというものです。このパターンにより、削除と再挿入の両方で大量の WAL が生成されます。代わりに、変更された行のみを更新または挿入するようにしてください。
大量の書き込みのバッチ化
Heroku では、INSERT
を直接使用するよりも、COPY
やその他のメカニズムを使用してデータを一括挿入することを強く推奨しています。クエリのたびに 1 行を操作することは、生成される WAL が増幅する原因になります。
データベースへのデータ投入に関する Postgres のドキュメントには、COPY
を使用した一括挿入や、一括データインポートのためのその他のベストプラクティスに関する詳しい説明があります。すべてのアドバイスが Heroku Postgres に適用できるわけではありません。
トリガーの無効化による書き込みの増幅
Postgres トリガーは強力なツールですが、1 つのテーブルへの書き込みが他のテーブルへの多数の書き込みを誘発する書き込み増幅につながる可能性があります。大量のデータを更新する必要がある場合、このパターンは良くありません。データのロードや大量の書き込みを実行するときはトリガーを無効にすることをお勧めします。トリガーの無効化は、特定のトリガーに対しては ALTER TABLE <table> DISABLE TRIGGER <trigger name>
によって、すべてのトリガーに対しては ALTER TABLE <table> DISABLE TRIGGER all
によって実行できます。
Postgres をオブジェクトストアとして使用しない
Postgres は、データのオンライントランザクション処理のために設計されたトランザクショナルリレーショナルデータベースであり、オブジェクトストアとしては設計されていません。したがって、バイナリデータや、大量のテキスト/JSON/JSONB を 1 列に格納するのはアンチパターンです。
一時テーブルまたはログに記録されないテーブルをデータのロードに使用する
大量のデータをロードする場合、WAL に書き込まれないテーブルにデータを “ステージング” すると役立つ場合があります。これらのテーブルはクラッシュに対して安全なテーブルではないため、このプロセスには一定のリスクがありますが、Extract-Transform-Load プロセスの一環として有用なツールになる可能性があります。
一時テーブルは CREATE TEMPORARY TABLE
を使用して、ログに記録されないテーブルは CREATE UNLOGGED TABLE
を使用して作成できます。
フォロワーのパリティ保証
Heroku Postgres のフォロワーは、リーダーよりも最大 2 プラン下位である可能性があります。プランのサイズが一致しないと、フォロワーの処理が WAL の再生速度に追いつかない可能性が高くなります。