web-dev-qa-db-ja.com

PostgreSQLクエリは制限1で非常に遅い

limit 1を追加すると、クエリが非常に遅くなります。

オブジェクトのタイムスタンプ付きの値を持つテーブルobject_valuesがあります:

 timestamp |  objectID |  value
--------------------------------
 2014-01-27|       234 | ksghdf

オブジェクトごとに最新の値を取得したい:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC LIMIT 1;

(10分以上後にクエリをキャンセルしました)

特定のobjectIDに値がない場合、このクエリは非常に低速です(結果がある場合は高速です)。制限を削除すると、結果がないことがほぼ瞬時にわかります。

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC;  
...  
Time: 0.463 ms

limit 1を含むクエリはインデックスを使用しないため、無制限のクエリはインデックスを使用することを説明しています。

クエリが遅い:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 1;  
QUERY PLAN`
----------------------------------------------------------------------------------------------------------------------------
Limit  (cost=0.00..2350.44 rows=1 width=126)
->  Index Scan Backward using object_values_timestamp on object_values  (cost=0.00..3995743.59 rows=1700 width=126)
     Filter: (objectID = 53708)`

高速クエリ:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC;
                                                  QUERY PLAN
--------------------------------------------------------------------------------------------------------------
 Sort  (cost=6540.86..6545.11 rows=1700 width=126)
   Sort Key: timestamp
   ->  Index Scan using object_values_objectID on working_hours_t  (cost=0.00..6449.65 rows=1700 width=126)
         Index Cond: (objectID = 53708)

テーブルには、44,884,559行と66,762の異なるobjectIDが含まれます。
両方のフィールドに別々のインデックスがあります:timestampobjectID
テーブルでvacuum analyzeを実行し、テーブルのインデックスを再作成しました。

さらに、制限を3以上に設定すると、スロークエリが高速になります。

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 3;
                                                     QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Limit  (cost=6471.62..6471.63 rows=3 width=126)
   ->  Sort  (cost=6471.62..6475.87 rows=1700 width=126)
         Sort Key: timestamp
         ->  Index Scan using object_values_objectID on object_values  (cost=0.00..6449.65 rows=1700 width=126)
               Index Cond: (objectID = 53708)

一般的には、実行コストに関してプランナーが誤った仮定をしているため、実行プランを遅くすることに関係していると思います。

これが本当の理由ですか?これに対する解決策はありますか?

36
pat

行の相関関係に関する統計の欠如に関連する問題に直面しています。これが最新バージョンのPostgresを使用している場合、参照用にpg-bugsに報告することを検討してください。

あなたの計画のために私が提案する解釈は次のとおりです。

  • limit 1は、Postgresが単一の行を探すようにし、その際、object_idが十分に一般的であり、インデックススキャンで適度にすばやく表示されると想定します。

    あなたが考えた統計に基づいて、おそらく、適合する行を見つけるために平均で〜70行を読む必要があるでしょう。 object_idとタイムスタンプがテーブルの大部分を実際に読み取ろうとしているポイントと相関していることを認識していません。

  • 対照的に、limit 3は、それが十分に珍しいことを認識させます。したがって、期待するobject_idを使用して、期待される1700行を_nソートすることを真剣に検討します。おそらく安くなります。

    たとえば、これらの行の分布により、すべての行がディスク上の同じ領域にパックされることがわかっている場合があります。

  • limit句がないと、とにかく1700をフェッチすることになるので、object_idのインデックスに直接進みます。

解決策、ところで:(object_id, timestamp)または(object_id, timestamp desc)にインデックスを追加します。

31

不要なORDER BY句をクエリに追加することにより、この問題を回避できます。

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp, objectID DESC limit 1;
39
Brendan Nee

更新が多いテーブルで同様の症状が出始めましたが、私の場合に必要なのは

analyze $table_name;

この場合、統計を更新する必要があり、それにより発生していたクエリプランが遅くなりました。
サポートドキュメント: https://www.postgresql.org/docs/current/sql-analyze.html

1
Dan Tanner