web-dev-qa-db-ja.com

PostgreSQL NOT IN arrayスロークエリ

数百万行の大きなテーブルがあります。各行には配列フィールドtagsがあります。 tagsにも適切なGINインデックスがあります。

タグのある行のカウントは高速です(〜7秒):

SELECT COUNT(*) FROM "subscriptions" WHERE (tags @> ARRAY['t1']::varchar[]);

ただし、タグのない行のカウントは非常に遅くなります(〜70秒)。

SELECT COUNT(*) FROM "subscriptions" WHERE NOT (tags @> ARRAY['t1']::varchar[]);

私は他のバリアントも試しましたが、同じ結果(〜70s)でした:

SELECT COUNT(*) FROM "subscriptions" WHERE NOT ('t1' = ANY (tags));

「アレイ外」操作を高速にするにはどうすればよいですか?

3
collimarco

Pgsql-performanceメーリングリストで thanks to Jeff Janes を解決しました:

GINインデックスは、「NOT」操作のためにPostgreSQLによって使用されませんでした。配列全体にBtreeインデックスを作成すると問題が解決し、インデックスのみのスキャンが可能になりました。これで、クエリは数分ではなく数ミリ秒しかかかりません。

3
collimarco

「t1」がまれなタグである場合、タグのない行をカウントすると、ほとんどの「数百万行」がカウントされます。また、「t1」が非常に一般的である場合でも、インデックスから数パーセントを超える行をカウントしても、シーケンシャルスキャンよりも改善されません。いずれにせよ、これが非常に速くなることは決してありません。インデックスは役に立ちません。

まれなタグを除いていくつかのカウントを行う必要があり、その間に行の総数が変わらない場合(または最小限の変更は問題ではない場合)、可能な行数の合計を1回取得する(低速)ことと、行の(小さい)カウントタグで減算(一致するインデックスで高速)...

正確な要件と完全なユースケースに応じて、他のショートカットがある場合があります。見る:

結論として、インデックスは通常、比較的少ない割合のテーブル行の識別にのみ役立ちます。ところで、IN= ANY()および包含演算子@>は関連ツールですが、微妙な違いがあります。 GINインデックスは通常、適切な配列演算子のみをサポートします。見る:

整数配列を演算子と組み合わせて使用​​すると、追加の intarray モジュールによって提供される演算子クラスに基づいたインデックスが得られる場合があります。高度に最適化されていますが、上記の原則に逆らうことはできません。

次に、any mixture of tags that the row must have or must not haveでコメントしたように query_int

1

あなたはすでに良い答えを持っているので、ここで検討のために食べ物の下に提出するもう少しです。最初に、あなたの質問は私に興味深いサウンドのテクニックを思い出させました:

https://heap.io/blog/engineering/running-10-million-postgresql-indexes-in-production

そのような戦略を試した人からのコメントに興味があります。

別の考えとして、別のオプションは、タグとその出現について独自の頻度表を維持することです。これにより、独自のコードジェネレーターを導くための情報が得られます。ここでの考え方は、genericクエリプランナー/オプティマイザは、あなたのspecificデータについてこれほど多くを知ることができないということです。頻度カウントを使用すると、かなり適切な概算カウントであっても、さまざまなケースでPostgresに送信するさまざまなクエリを構築できます。

その頻度カウントのアイデアを具体化する

元の簡単な答えが明確ではなかったため、ここで少し詳しく説明します。ここでの概念は、一意のタグとカウントを含むtag_countのような頻度カウントのテーブルを維持できるということです。その小さなデータは、クエリ内の一般的なタグがbefore Postgresの実際のクエリを生成する方法をテストする機能を提供します。この「単純な」計画はいくつかの事柄に依存しますが、どれもあなたの場合には当てはまらない場合があります。

  • クエリを構成するコードがあり、これを変更して、この前処理を行って、クエリを構成する最適な方法を見つけることができます。

  • 頻度カウントを使用して、プランナーがより良い仕事をするのを助ける方法を見つけることができます。

  • 頻度カウント更新コードを実行する方法はいくつかあります。

  • 適切な忠実度で、システムを停止することなくカウントを維持する方法がいくつかあります。

その最後のポイントは明らかに大きなトピックです。最も単純な方法(概念上)は、古いタグと新しいタグを見つけ、それに応じてカウントを調整する追加/変更/削除のトリガーです。最もパフォーマンスの高いソリューションではなく、ボトルネックになる可能性があります。多くの代替設計があります。 (post-and-reconcileキューテーブルを使用したステートメントレベルのトリガーは、ボトルネックにならない代替設計になります。)正直なところ、Postgresの増分更新の最適な実行方法はまだわかりません。私は数か月前に自分のために〜10の戦略をスケッチしましたが、ソリューションのテストと比較に戻っていません。このフォーラムの他の人々はPostgresを長い間使用しており、非常に賢く、役に立ちます。したがって、この種のソリューションがあなたの望んでいるものである場合、もう一度尋ねる価値があります。

1
Morris de Oryx