使用されていないがクエリに影響を与えるインデックス


8

いくつかの数値といくつかの追加データを含むPostgreSQL 9.3テーブルがあります。

CREATE TABLE mytable (
    myid BIGINT,
    somedata BYTEA
)

このテーブルには現在約1,000万のレコードがあり、1GBのディスク容量を使用します。myid連続していません。

100000の連続番号の各ブロックにある行の数を計算したいと思います。

SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;

これは約3500行を返します。

クエリプランでまったく言及されていなくても、特定のインデックスが存在すると、このクエリが大幅に高速化されることに気づきました。インデックスなしのクエリプラン:

db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=1636639.92..1709958.65 rows=496942 width=8) (actual time=6783.763..8888.841 rows=3460 loops=1)
   Output: ((myid / 100000)), count(*)
   ->  Sort  (cost=1636639.92..1659008.91 rows=8947594 width=8) (actual time=6783.752..8005.831 rows=8947557 loops=1)
         Output: ((myid / 100000))
         Sort Key: ((mytable.myid / 100000))
         Sort Method: external merge  Disk: 157440kB
         ->  Seq Scan on public.mytable  (cost=0.00..236506.92 rows=8947594 width=8) (actual time=0.020..1674.838 rows=8947557 loops=1)
               Output: (myid / 100000)
 Total runtime: 8914.780 ms
(9 rows)

インデックス:

db=> CREATE INDEX myindex ON mytable ((myid/100000));
db=> VACUUM ANALYZE;

新しいクエリプラン:

db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
                                                            QUERY PLAN                                                            
----------------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=281242.99..281285.97 rows=3439 width=8) (actual time=3190.189..3190.800 rows=3460 loops=1)
   Output: ((myid / 100000)), count(*)
   ->  Seq Scan on public.mytable  (cost=0.00..236505.56 rows=8947485 width=8) (actual time=0.026..1659.571 rows=8947557 loops=1)
         Output: (myid / 100000)
 Total runtime: 3190.975 ms
(5 rows)

したがって、クエリプランとランタイムは大幅に(ほぼ3倍)異なりますが、どちらもインデックスについては言及していません。この動作は、私の開発マシンで完全に再現可能です。インデックスを削除し、クエリを数回テストし、インデックスを再作成し、再度クエリを数回テストするというサイクルを数回行いました。ここで何が起こっているのですか?


私はPostgresのクエリプランを分析する専門家ではありませんが、HashAggregateメソッドにインデックスが使用されている(そしてソートが不要)と思います。これにより、パフォーマンスが向上します。なぜインデックスが計画に記載されていないのか、私には手掛かりがありません。
ypercubeᵀᴹ

次を使用して詳細モードを有効にすると、計画の出力は変わりますexplain (analyze true, verbose true) ...か?
a_horse_with_no_name 2014

これを自己完結型のテストケースにまとめることができればすばらしいと思います。それは確かに奇妙に思えます。
クレイグリンガー

@a_horse_with_no_name:はい、変更されます。質問のクエリプランを詳細なプランに置き換えました。しかし、そのクエリプランはまだインデックスについてまったく言及していません。
liori 14

インデックスのあるid列に利用可能な統計(特にカーディナリティと最小値/最大値)がある場合よりも多い場合、インデックスをまったく使用しない場合でも、メソッドの選択によってオプティマイザのグループが変更される可能性があります。 。(私はpostgresのオプティマイザと統計をまったく知りません。そのため、それが当てはまるかどうかはわかりません。)
Mat

回答:


3

VACUUM ANALYZEあなたの例で違いを生みます。さらに、@ jjanesが提供したように、関数インデックスの追加の統計。ドキュメントごと:

pg_statisticインデックス式の値に関する統計データも格納します。これらは、実際のデータ列であるかのように記述されます。特に、starelidインデックスを参照します。ただし、基になるテーブル列のエントリと重複するため、通常の非式インデックス列のエントリは作成されません。

ただし、インデックスを作成するだけでは、Postgresは統計を収集しません。試してください:

CREATE INDEX myindex ON mytable ((myid/100000));
SELECT * FROM pg_statistic WHERE starelid = 'myindex'::regclass;

最初に実行するANALYZE(またはVACUUM ANALYZE、またはautovacuumデーモンが起動する)までは何も返しません。

ANALYZE mytable;
SELECT * FROM pg_statistic WHERE starelid = 'myindex'::regclass;

追加された統計が表示されます。

とにかくテーブル全体を読み取る必要があるので、Postgresは、計算がmyid/100000切り替えに十分なコストがかかると予想しない限り、順次スキャンを使用します。

他の唯一のチャンスは、インデックスがテーブルよりもはるかに小さい場合、インデックスのみのスキャンであり、インデックスのみのスキャンの前提条件が満たされます。詳細はPostgres Wikiマニュアルにあります

その機能的指標が使用されない限り、追加された統計による付随的な利益は中程度です。テーブルが読み取り専用の場合、コストは低くなりますが、再び、おそらくインデックスのみのスキャンがすぐに表示されます。

より高い統計目標をに設定することで、より良いクエリプランを達成できる場合もありますmytable.myid。それはわずかなコストしかかかりません。もっと:


この説明をありがとう、問題を理解するのに非常に役立ちます。私の場合、追加のmyid/100000 BETWEEN somevalue AND othervalue条件が必要になる可能性が高いため、とにかくインデックスをクエリプランで使用します。テーブル全体でインデックスが役立つ理由がわからなかったので、この質問をしました。
liori 14

@liori:(タイプに応じて丸め効果を検討する)でそれをカバーできWHERE myid BETWEEN somevalue*100000 AND othervalue*100000、おそらくにプレーンインデックスがすでにあるmyidので、特別なインデックスを追加しなくても実行できます。より効率的かもしれません。
Erwin Brandstetter 2014

6

式インデックスを作成すると、PostgreSQLはその式に関する統計を収集します。これらの統計が手元にあるため、クエリが返す集計行数の正確な見積もりが得られるため、より適切なプランを選択できます。

特にこの場合、追加の統計がなければ、ハッシュテーブルが大きすぎてwork_memに収まらないと考えたため、その方法を選択しませんでした。


プランナーは価値work_memを考慮していないと思います。並べ替えがメモリに収まるように値を上げた場合でも、同じ計画が使用されます。ここで、時間差(そのほとんど)は外部ディスクのソートに起因することに注意してください。
dezso 2014

1
@dezsoソートをメモリに収めるために必要なwork_memの値を実験的に2倍または3倍にするとどうなるでしょうか。並べ替えとハッシュではオーバーヘッドの見積もりが異なり、見積もり自体はあまり正確ではありません。また、使用している9.3のマイナーバージョンは何ですか?
jjanes 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.