web-dev-qa-db-ja.com

小さなテーブルは、強制的なVACUUMによって修正され、極端なパフォーマンス低下を引き起こします。どうして?

PostgreSQL 9.6を使用しています。

17個のテーブルを結合するクエリがあります。そのうち9個は数百万行あります。クエリは正常に実行されていましたが、今週パフォーマンスが急速に低下しました。 EXPLAINの出力は役に立ちませんでした(すべてのスキャンは非常に小さなテーブルを除いてインデックススキャンです)、パフォーマンスの低下を引き起こしたテーブルを特定するためにクエリからテーブルを削除する必要がありました。

40行を含む目立たないテーブルがクエリを壊したことがわかりました。テーブルがない場合は800ミリ秒、テーブルを使用しない場合は30秒です。テーブルでVACUUM FULLを実行したところ、約1秒で実行されましたが、パフォーマンスは通常に戻りました。

私の質問:

  1. <10kbのテーブルがこのようなパフォーマンスを損なうのは何が原因でしょうか?
  2. 将来同じ問題を回避する方法は?

デバッグの過程で、別のサーバーにベースバックアップを作成したので、DBのファイルシステムレベルのコピーが2つあり、そのうちの1つはVACUUM FULLを実行しなかったものです。 pgAdminを使用して、真空にされていないコピーにログオンすると、次のメッセージが表示されました。

テーブル "public.clients"の推定行数は、実際の行数と大きく異なります。このテーブルでVACUUM ANALYZEを実行する必要があります。

真空引きされていないテーブルには、40行がカウントされ、0行が推定されます。残りの統計情報をスクリーンショットで示します。

pgAdmin stats

6
Julien

テーブルは小さいかもしれませんが、Postgresがおおよそ行を期待している限り、おおよそのものとは異なるクエリプランを選択する可能性があります- 40 rows-同じクエリプランはそれほど効率的ではありません。

結合multiply結果行にaddingするだけではなく、結合するため、小さなテーブルの40行は結合時に大きな影響を与える可能性がありますあなたの例のように数百万行の大きなテーブルにこの違いは、実行時間の30倍を簡単に説明できます。
または マニュアルによると

適度に正確な統計を取得することが重要です。そうしないと、計画の選択が不十分なためにデータベースのパフォーマンスが低下する可能性があります。

デフォルト autovacuum ほとんどのインストールでは設定は問題ありません。検討してください:

しかし、数百万行の複数のテーブルを保持するデータベースの場合、選択したテーブルのテーブルごとの設定と、DB全体の手動のANALYZEを時々調整することを検討します。

残りの質問

Q1。 autovacuumがANALYZEを自動的に起動しないのはなぜですか?
Q2。なぜVACUUM FULLが問題を解決したのですか?

Q2は単純です。他の重要な統計はANALYZEによってのみ更新されますが、pg_class.reltuplesの基本的なカウント推定は更新されますより頻繁に。 マニュアル:

テーブルの行数。これは、計画担当者が使用する推定にすぎません。 VACUUMANALYZE、およびCREATE INDEXなどのいくつかのDDLコマンドによって更新されます。

Q1はより洗練されています。

再びマニュアル

デーモンはANALYZEを、挿入または更新された行数の関数として厳密にスケジュールします。それが意味のある統計的変化につながるかどうかについては知りません。

(特に)関連する設定:

autovacuum_analyze_thresholdinteger

1つのテーブルでANALYZEをトリガーするために必要な挿入、更新、または削除されたタプルの最小数を指定します。デフォルトは5タプルです。このパラメーターは、postgresql.confファイルまたはサーバーのコマンドラインでのみ設定できます。ただし、テーブルストレージパラメータを変更することで、個々のテーブルの設定を上書きできます。

autovacuum_analyze_scale_factorfloating point

ANALYZEをトリガーするかどうかを決定するときにautovacuum_analyze_thresholdに追加するテーブルサイズの割合を指定します。デフォルトは0.1(テーブルサイズの10%)です。このパラメーターは、postgresql.confファイルまたはサーバーのコマンドラインでのみ設定できます。ただし、テーブルストレージパラメータを変更することで、個々のテーブルの設定を上書きできます。

大胆な強調鉱山。

デモ

アーティファクトのテストを回避するために、テストDBがほとんどアイドル状態であり、デフォルト設定で実行していることを確認してください。

SELECT * FROM pg_settings WHERE name ~ '^autovacuum|track_counts';

最も重要なこと:

autovacuum_analyze_scale_factor = 0.1
autovacuum_analyze_threshold = 50
autovacuum_naptime = 60
track_counts = on

基本的に、autovacuumは、テーブルにlast_estimate/ 100 + 50行が変更されているかどうかを1分ごとにチェックし、それらのANALYZEを起動します。

あなたのケースで何が起こったかを理解するには:

CREATE TABLE t50 (id int primary key, foo text);
INSERT INTO t50 SELECT g, 'txt' || g FROM generate_series(1,50) g;
SELECT reltuples FROM pg_class WHERE oid = 't50'::regclass;

pg_class.reltuplesは、テーブルの推定行数です。詳細はこちら:

0を取得します。 2分間待って、1分の遅延を確実に超えるようにします。再び確かめる。まだ0。もう1行挿入して、もう一度確認します。

INSERT INTO t50 VALUES (51, 'txt51 triggers analyze');
SELECT reltuples FROM pg_class WHERE oid = 't50'::regclass;

まだ0。さらに2分待ってから、もう一度確認してください。多田! 51の更新されたカウントが表示されます。 51の行が挿入(または更新または削除)されるまで、自動バキュームは作動しませんでした。

詳細(last_autoanalyzeのタイムスタンプを含む)を表示するには:

SELECT * FROM pg_stat_all_tables WHERE relid = 't50'::regclass;

関連:

解決

ANALYZE上でpublic.clientsを手動で1回(またはDB全体で安価)実行し、この重要なテーブルに対してより積極的なテーブルごとの自動バキューム設定を使用します。お気に入り:

ALTER TABLE public.clients SET (autovacuum_analyze_scale_factor = 0.01
                              , autovacuum_analyze_threshold = 10);

他の理由で、大きなテーブルの一部の設定を監査することもできます。比較:

また重要

17テーブルに参加しています。これはjoin_collapse_limitのデフォルト設定8。明示的な結合構文を使用し(おそらく既に使用している場合があります)、クエリを書き直して、最も選択的なテーブル(または最も選択的な述語を持つテーブル)をSELECTリストの最初に配置します。関連:


PS:上記のテストの実行中に、ドキュメントのマイナーなバグを見つけたと思います。 autovacuum_analyze_thresholdのマニュアルには次のように記載されています:

最小 1つのテーブルでANALYZEをトリガーするために必要な挿入、更新、または削除されたタプルの数を指定します

これは50がトリガーANALYZEを挿入することを示し、51私が観察したように。 pg_settings.short_descで類似:

分析前のタプルの挿入、更新、または削除の最小数。

実際、autovacuum このマニュアルでは の説明は私の観察と一致します:

それ以外の場合、最後のVACUUMexceeds「真空しきい値」以降に廃止されたタプルの数であれば、テーブルは真空になります。

最初の2つの文は少し間違っているようです。
バグレポートを提出しました。

6