大きなテーブルでのインデックススキャンが遅い


11

PostgreSQL 9.2を使用すると、比較的大きなテーブル(2億を超える行)でクエリが遅くなるという問題が発生します。クレイジーなことは何もしていません。単に歴史的な価値を加えているだけです。以下は、クエリとクエリプランの出力です。

私のテーブルレイアウト:

                                   Table "public.energy_energyentry"
  Column   |           Type           |                            Modifiers
-----------+--------------------------+-----------------------------------------------------------------
 id        | integer                  | not null default nextval('energy_energyentry_id_seq'::regclass)
 prop_id   | integer                  | not null
 timestamp | timestamp with time zone | not null
 value     | double precision         | not null
Indexes:
    "energy_energyentry_pkey" PRIMARY KEY, btree (id)
    "energy_energyentry_prop_id" btree (prop_id)
    "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp")
Foreign-key constraints:
    "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED

データは2012-01-01から現在までの範囲で、新しいデータが常に追加されています。prop_id外部キーには約2.2kの異なる値があり、均等に分散されています。

行の見積もりはそれほど遠くないことに気づきましたが、コストの見積もりは係数4倍大きくなっています。これはおそらく問題ではありませんが、それについて私ができることはありますか?

テーブルが常にメモリにあるわけではないので、ディスクアクセスが問題になる可能性があると思います。

EXPLAIN ANALYZE 
SELECT SUM("value") 
FROM "energy_energyentry" 
WHERE 
  "prop_id"=82411 
  AND "timestamp">'2014-06-11' 
  AND "timestamp"<'2014-11-11'
;
 Aggregate  (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1)
   ->  Index Scan using energy_energyentry_prop_id_timestamp_idx on  energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1)
         Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone))
 Total runtime: 51504.841 ms

これをより速くするための提案はありますか?
変なことは何もしなかったと聞いただけでも大丈夫です。


1
テーブルの外観、インデックス、データの広がりを教えてください。
Colin 't Hart

あなたが尋ねた追加の情報提供者を追加しました。何も見逃したのか。
Exelian 2014年

2
奇妙なこと:あなたの説明分析は示しprop_time_idxていますが、テーブル定義は示していますentry_prop_id_timestamp_idx。これは同じインデックスですか?修正してください。
Colin 't Hart

あなたがで参照している場合という事実に「費用の見積もりは、ファクタ4倍大きいように思われる」コストの番号が4倍程度のものであり、実際の時間、2つが互いに何の関係もないという通知をしてください。コストは推定にすぎず、クエリオプティマイザーが最適なプランを選択するのに役立ちます。このコンテキストの外では、通常は意味のない値です。
dezso 2014年

1
テーブルの何パーセントが(の値を考慮せずに)日付範囲で表されpropますか?ほんの少しの割合であれば、おそらくインデックス("timestamp", prop)がより良いでしょう。prop多くの場合、先頭列が同じ(複数の場合)の複数のインデックスも冗長です。
Colin 't Hart

回答:


9

テーブルが大きいため、テーブル全体にわたるインデックスも大きい。仮定して:

  • 新しいデータ(timestamp = now())のみが入力されます
  • 既存の行は変更も削除もされません。
  • 2012-01-01以降のデータがありますが、クエリは主に現在の年(?)

部分的な複数列(カバーする!)インデックスをお勧めします

CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value)
WHERE "timestamp" >= '2014-01-01 0:0';  -- adapt to your needs

定期的に照会される時間範囲のみを含めてください。新しいエントリを追加すると、時間の経過とともに有効性が低下します。時々インデックスを再作成してください。(クエリを調整する必要がある場合があります。)以下のリンクされた回答を参照してください。

最後の列の値は、これからインデックスのみのスキャンを取得するためにのみ含まれています。積極的な自動バキューム設定は、すでに説明した@jjanesのように、可視性マップを最新の状態に保つことで役立つ場合があります。

部分インデックスは、より簡単にRAMに収まり、そこに長く留まる必要があります。

次のWHEREように、インデックスがクエリに適用可能であることをプランナーに理解させるために、クエリにこの条件を含める必要がある場合があります。

SELECT sum(value) AS sum_value
FROM   energy_energyentry
WHERE  prop_id = 82411 
AND   "timestamp" > '2014-06-11 0:0' 
AND   "timestamp" < '2014-11-11 0:0'
AND   "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed

クエリは多くの行(rows=13578)を合計しているため、インデックスのみのスキャンでも、これには時間がかかります。ただし、50秒近くになることはありません。中途半端なハードウェアでは1秒未満。

関連(ただし、とを無視CLUSTERFILLFACTORます。これからインデックスのみのスキャンを取得できる場合、どちらも無関係です)

余談:現在にインデックスがある
ので、に追加のインデックスを追加すると、それだけの価値がある場合があります。(prop_id, "timestamp")(prop_id)


PostgresがBRINインデックスをサポートするようになったので、それはここで役に立ちますか?postgresのデータに約1億4,000万行を格納する予定ですが、BRINはテーブルに使用するのに適切なインデックスですか?
Arya

2

(prop_id、 "timestamp"、 "value")にインデックスを作成すると、インデックスのみのスキャンを使用して、テーブルにアクセスすることなく値を計算できます。これにより、多くのランダムディスクアクセスを節約できます。

最大限の利益を得るには、テーブルを掃除機で掃除する必要があります。デフォルトのautovac設定は、インデックスのみのスキャンを効率的にサポートしたい挿入のみのテーブルに対して十分に積極的ではありません。


値を追加することは確かに興味深いかもしれませんが、それがスピードアップするかどうかを調べます。私が見ることができる真空設定やドキュメントについて何か提案はありますか?
Exelian 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.