数週間前、自動真空調整の基本について説明しました。その投稿の終わりに、私はすぐに真空引きの問題を調査することを約束しました。さて、予定より少し時間がかかりましたが、どうぞ。
簡単に要約すると、autovacuum デッドローをクリーンアップするバックグラウンドプロセスです。古い削除された行バージョン。 VACUUMを実行して、手動でクリーンアップを実行することもできます。 、ただしautovacuum これは、テーブル内のデッド行の量に応じて、適切なタイミングで自動的に実行されます。あまり頻繁ではありませんが、「ガベージ」の量を制御するのに十分な頻度です。
一般的に、autovacuum あまり頻繁に実行することはできません。クリーンアップは、テーブルにデッド行がいくつも蓄積された後にのみ実行されます。ただし、さまざまな理由で遅延する可能性があり、その結果、テーブルとインデックスが必要以上に大きくなります。そして、それはまさにこの投稿のトピックです。では、一般的な原因とその特定方法は何ですか?
スロットル
チューニングの基本で説明されているように、autovacuum ワーカーは、時間間隔ごとに特定の量の作業のみを実行するように調整されます。デフォルトの制限はかなり低く、書き込みは約4MB /秒、読み取りは8MB/秒です。これは、Raspberry Piのような小さなマシンや10年前の小さなサーバーに適していますが、現在のマシンははるかに強力で(CPUとI / Oの両方の点で)、はるかに多くのデータを処理します。
いくつかの大きなテーブルといくつかの小さなテーブルがあると想像してください。 3つすべてのautovacuum 作業者は大きなテーブルのクリーンアップを開始します。蓄積されたデッドローの量に関係なく、小さなテーブルはどれも真空になりません。十分な監視があれば、これを特定することは特に難しくありません。すべてのautovacuumが発生する期間を探します 多くのデッドローが蓄積されているにもかかわらず、テーブルが空にされていない間、ワーカーは忙しいです。
必要な情報はすべてpg_stat_activityにあります (autovacuumの数 ワーカープロセス)およびpg_stat_all_tables (last_autovacuum およびn_dead_tup 。
autovacuumの数を増やす 作業の総量は同じままであるため、労働者は解決策ではありません。テーブルごとのスロットリング制限を指定できますが、そのワーカーを合計制限から除外することはできますが、それでも必要なときに利用可能なワーカーが存在することを保証するものではありません。
適切な解決策は、ハードウェア構成とワークロードパターンに関して妥当な制限を使用して、スロットリングを調整することです。いくつかの基本的なスロットルの推奨事項は、前の投稿で言及されています。 (明らかに、データベースで生成されるデッド行の量を減らすことができれば、それは理想的な解決策になります。)
この時点から、スロットルは問題ではないと想定します。つまり、autovacuum ワーカーは長期間飽和状態ではなく、不当な遅延なしにすべてのテーブルでクリーンアップがトリガーされます。
長いトランザクション
ですから、定期的にテーブルを掃除機で掃除すれば、確かに多くの死んだ列を蓄積することはできませんよね?残念だけど違う。行が削除された直後に実際に「削除可能」になるのではなく、行が表示される可能性のあるトランザクションがない場合に限ります。正確な動作は、他のトランザクションが実行していることとシリアル化レベルによって異なりますが、一般的には次のようになります。
コミット済み
- クエリの実行はクリーンアップをブロックします
- アイドルトランザクションは、書き込みを実行した場合にのみクリーンアップをブロックします
- アイドル状態のトランザクション(書き込みなし)はクリーンアップをブロックしません(ただし、とにかくそれらを維持することはお勧めできません)
シリアル化可能
- クエリの実行はクリーンアップをブロックします
- アイドルトランザクションはクリーンアップをブロックします(読み取りのみを行った場合でも)
もちろん実際にはもっと微妙な違いがありますが、さまざまなビットをすべて説明するには、最初にXIDとスナップショットがどのように機能するかを説明する必要があり、それはこの投稿の目的ではありません。これから本当に取り除くべきことは、特にそれらのトランザクションが書き込みを行った可能性がある場合、長いトランザクションは悪い考えであるということです。
もちろん、トランザクションを長期間保持する必要がある場合がある完全に正当な理由があります(たとえば、すべての変更に対してACIDを確保する必要がある場合)。ただし、不必要に発生しないように注意してください。アプリケーションの設計が不十分なため。
autovacuumが原因で、CPUとI/Oの使用率が高くなるという予想外の結果が発生します。 デッドロー(またはそれらのいくつか)をクリーンアップせずに、何度も実行します。そのため、テーブルは次のラウンドでもクリーンアップの対象となり、善よりも害をもたらします。
これを検出する方法は?まず、長時間実行されるトランザクション、特にアイドル状態のトランザクションを監視する必要があります。 pg_stat_activityからデータを読み取るだけです。 。ビューの定義はPostgreSQLのバージョンによって少し変わるため、これを少し調整する必要があるかもしれません:
SELECT xact_start, state FROM pg_stat_activity; -- count 'idle' transactions longer than 15 minutes (since BEGIN) SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'idle in transaction' AND (now() - xact_start) > interval '15 minutes' -- count transactions 'idle' for more than 5 minutes SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'idle in transaction' AND (now() - state_change) > interval '5 minutes'
また、既存の監視プラグインを使用することもできます。 check_postgres.pl。それらには、このタイプの健全性チェックがすでに含まれています。アプリケーション固有の適切なトランザクション/クエリ期間を決定する必要があります。
PostgreSQL 9.6以降では、idle_in_transaction_session_timeoutも使用できます。 長時間アイドル状態のトランザクションが自動的に終了するようにします。同様に、長いクエリにはstatement_timeoutがあります 。
もう1つの便利なものはVACUUM VERBOSE これにより、まだ削除できなかったデッド行の数が実際にわかります:
db=# VACUUM verbose z; INFO: vacuuming "public.z" INFO: "z": found 0 removable, 66797 nonremovable row versions in 443 out of 443 pages DETAIL: 12308 dead row versions cannot be removed yet. ...
どのバックエンドがクリーンアップを妨げているかはわかりませんが、何が起こっているかをかなり明確に示しています。
注: 。 autovacuumからこの情報を簡単に取得することはできません DEBUG2でのみログに記録されるため デフォルトでは(そして、本番環境ではそのログレベルで実行したくないはずです)。
ホットスタンバイでの長いクエリ
テーブルがタイムリーにバキュームされているが、デッドタプルが削除されておらず、テーブルとインデックスが肥大していると仮定しましょう。 pg_stat_activityを監視しています 長期にわたるトランザクションはありません。何が問題になる可能性がありますか?
ストリーミングレプリカがある場合は、問題が発生している可能性があります。レプリカがhot_standby_feedback=onを使用する場合 、レプリカに対するクエリは、クリーンアップのブロックを含め、プライマリ上のトランザクションとほとんど同じように機能します。もちろん、hot_standby_feedback=on レプリカで長いクエリ(分析やBIワークロードなど)を実行するときに正確に使用され、レプリカの競合によるキャンセルを防ぎます。
残念ながら、選択する必要があります– hot_standby_feedback=onを維持する クリーンアップの遅延を受け入れるか、キャンセルされたクエリを処理します。 max_standby_streaming_delayを使用することもできます 影響を制限しますが、キャンセルを完全に防ぐことはできません(したがって、クエリを再試行する必要があります)。
実際、現在3番目のオプションである論理レプリケーションがあります。 BIレプリカに物理ストリーミングレプリケーションを使用する代わりに、PostgreSQL 10で利用可能な新しい論理レプリケーションを使用して変更をコピーできます。論理レプリケーションは、プライマリとレプリカ間の結合を緩和し、クラスターをほぼ独立させます(独立してクリーンアップされます。など)。
これにより、物理ストリーミングレプリケーションに関連する2つの問題(プライマリクエリのクリーンアップの遅延またはBIレプリカのキャンセルされたクエリ)が解決されます。ただし、DRの目的で使用されるレプリカの場合、ストリーミングレプリケーションは依然として正しい選択です。ただし、これらのレプリカは長いクエリを実行していません(または実行すべきではありません)。
注: 論理レプリケーションはPostgreSQL10で利用可能になると述べましたが、インフラストラクチャのかなりの部分が以前のリリース(特にPostgreSQL 9.6)で利用可能でした。そのため、古いリリースでもこれを実行できる可能性がありますが(一部のお客様に対しては実行しました)、PostgreSQL10を使用するとはるかに便利で快適になります。
autoanalyzeの問題
あなたが見逃すかもしれない詳細は、そのautovacuumです ワーカーは実際には2つの異なるタスクを実行します。まず、クリーンアップ(VACUUMを実行しているかのように) )だけでなく、統計も収集します(ANALYZEを実行しているかのように) )。そして両方 パーツはautovacuum_cost_limitを使用して調整されます 。
ただし、トランザクションの処理には大きな違いがあります。 VACUUMのときはいつでも 一部がautovacuum_cost_limitに到達します 、ワーカーはスナップショットを解放し、しばらくスリープします。 ANALYZE ただし、単一のスナップショット/トランザクションで実行する必要があり、実行します。 ブロックのクリーンアップ。
これは、特にこれを行う場合は、足で自分を撃つためのエレガントな方法です:
-
default_statistics_targetを増やします より大きなサンプルからより正確な統計を構築するため - 下位の
autovacuum_analyze_scale_factorより頻繁に統計を収集する
もちろん、意図しない結果は、ANALYZE (VACUUMとは異なり、より頻繁に発生し、はるかに時間がかかります。 一部)クリーンアップを防止します。解決策は通常、かなり単純です。autovacuum_analyze_scale_factorを下げないでください。 過度に。 ANALYZEを実行しています ほとんどの場合、テーブルの変更の10%で十分です。
n_dead_tup
最後に言及したいのは、pg_stat_all_tables.n_dead_tupの変更についてです。 値。値は単純なカウンターであり、新しいデッドタプルが作成されるたびにインクリメントされ、クリーンアップされるたびにデクリメントされると考えるかもしれません。ただし、これは実際には、ANALYZEによって更新された死んだタプルの数の推定値にすぎません。 。 ANALYZEであるため、小さなテーブル(240MB未満)の場合、それほど大きな違いはありません。 テーブル全体を読み取るので、かなり正確です。ただし、大きなテーブルの場合、サンプリングされるテーブルのサブセットに応じて、かなり変化する可能性があります。そして、autovacuum_vacuum_scale_factorを下げます よりランダムになります。
したがって、n_dead_tupを見るときは注意してください 監視システムで。値の突然の低下または増加は、単にANALYZEが原因である可能性があります 別の見積もりを再計算しますが、実際のクリーンアップやテーブルに表示される新しいデッドタプルによるものではありません。
概要
これをいくつかの簡単なポイントに要約すると:
-
autovacuum死んだタプルを必要とする可能性のあるトランザクションがない場合にのみ、機能します。 - 実行時間の長いクエリは、クリーンアップをブロックします。
statement_timeoutの使用を検討してください ダメージを制限します。 - 実行時間の長いトランザクションにより、クリーンアップがブロックされる場合があります。正確な動作は、分離レベルやトランザクションで何が起こったかなどによって異なります。それらを監視し、可能であれば終了します。
-
hot_standby_feedback=onを使用したレプリカでの長時間実行クエリ クリーンアップもブロックされる可能性があります。 autoanalyzeスロットルもされますが、VACUUMとは異なります 一部、単一のスナップショットを保持します(したがって、クリーンアップをブロックします)。-
n_dead_tupANALYZEによって維持される単なる見積もりです 、したがって、多少の変動が予想されます(特に大きなテーブルの場合)。