4
CREATE TABLE product (
  product_id     SERIAL,
  factory_key    VARCHAR(60),
  relevant       BOOLEAN
)
Indexes:
"product_factory_key_key" btree (factory_key);
"product_factory_key_relevant_key" btree (factory_key, relevant) WHERE relevant = false;
"product_relevant_key" btree (relevant);

事実:

  1. productテーブルには約 1 億件のレコードがあります
  2. 少数の工場があります。たとえば、1 つの工場に 500 万個の製品があるとします。
  3. 何百万もの工場キーがあります
  4. 各ファクトリに関係のない行は少数です。たとえば、500 万個の製品を扱う工場があり、関連性のない製品が約 100 個あるとします。
  5. ただし、関係のない行が何百万行もあります。最も一般的なケースは、1 つのファクトリ キー、5 行の製品、おそらく 2 行の関連しないものです。

これは問題のクエリです:

SELECT * FROM product WHERE factory_key='some_product_key' AND relevant=false LIMIT 10;

説明分析:

                                                        QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.00..23.06 rows=10 width=188) (actual time=2709.654..32252.961 rows=10 loops=1)
   ->  Seq Scan on product  (cost=0.00..7366785.34 rows=3194759 width=188) (actual time=2709.634..32252.904 rows=10 loops=1)
         Filter: ((NOT relevant) AND ((product_key)::text = 'some_product_key'::text))
         Rows Removed by Filter: 449486
 Total runtime: 32253.150 ms
(5 rows)

問題:

これは次の理由で問題があります。

  1. このファクトリに一致する行が非常に多いため、プランナーは seq スキャンを使用することを選択したと思います。(約 320 万行がこのファクトリまたは約 3% に一致します)

  2. ただし、非常に少数の行のみが関連していないためです。そして、私は関連していないものを探しています。seq スキャンは非常に高価になります。

すでに複合インデックスproduct_factory_key_relevant_keyを作成しましたが、インデックスを利用していません。

編集:

postgres に複合キーの使用を強制しようとしています:product_factory_key_relevant_key

SET enable_seqscan=off

ただし、現在はインデックススキャンを使用しています。実際には、seqscan よりもまだ遅いです。(つまり、seq スキャンを実行するプランナーは正しかったと思います)

                                                                       QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.57..34.03 rows=10 width=188) (actual time=8.088..469974.692 rows=10 loops=1)
   ->  Index Scan using product_factory_key_relevant_key on product  (cost=0.57..10689307.49 rows=3194776 width=188) (actual time=8.083..469974.655 rows=10 loops=1)
         Index Cond: (relevant = false)
         Filter: ((NOT relevant) AND ((product_key)::text = 'some_product_key'::text))
         Rows Removed by Filter: 2205295
 Total runtime: 469974.820 ms
(6 rows)
4

2 に答える 2

1

私見、あなたのクエリの問題は$1. これは、準備されたステートメントを使用していることを意味すると思います。それがベストプラクティスであることをどこかで読んだからです。

PG は、使用している基準のカーディナリティが高いか低いかを事前に知ることができないため、これは実際には悪いことです。そのため、ほとんどのケースに適合するプランを選択する必要があります。btree インデックスの一番左の部分が役に立たないような方法でいくつかの値がいたるところにある場合は、seq スキャンを実行する十分な理由があります。

対照的に、最初に準備せずにクエリを実行すると、渡された値に基づいて計画が立てられ、その特定の値に最適な計画が選択されます。したがって、準備せずに同じクエリを実行することを検討してください。既存のインデックスを使用し始める場合は、この病理学的なユースケースになります。

そうでない場合、クレイグが言ったこと...特に部分インデックス。

于 2014-04-19T07:30:43.973 に答える