インデックススキャンではなくPostgreSQL順次スキャンなぜですか?


11

こんにちは、私はPostgreSQLデータベースクエリに問題があり、誰かが手伝ってくれるかどうか疑問に思っています。いくつかのシナリオでは、私のクエリは、2つのテーブルdataとを結合するために使用した、私が作成したインデックスを無視しているようdata_areaです。これが発生すると、シーケンシャルスキャンが使用され、クエリが非常に遅くなります。

順次スキャン(〜5分)

Unique  (cost=15368261.82..15369053.96 rows=200 width=1942) (actual time=301266.832..301346.936 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6086.77..610089.54 rows=321976 width=297) (actual time=26.286..197.625 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6006.27 rows=324789 width=0) (actual time=25.462..25.462 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=15368261.82..15368657.89 rows=158427 width=1942) (actual time=301266.829..301287.110 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=15174943.29..15354578.91 rows=158427 width=1942) (actual time=300068.588..301052.832 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Hash Join  (cost=15174792.93..15351854.12 rows=158427 width=684) (actual time=300066.288..300971.644 rows=155194 loops=1)
                     Hash Cond: (data.id = data_area.data_id)
                     ->  CTE Scan on data  (cost=0.00..6439.52 rows=321976 width=676) (actual time=26.290..313.842 rows=335130 loops=1)
                     ->  Hash  (cost=14857017.62..14857017.62 rows=25422025 width=8) (actual time=300028.260..300028.260 rows=26709939 loops=1)
                           Buckets: 4194304  Batches: 1  Memory Usage: 1043357kB
                           ->  Seq Scan on data_area  (cost=0.00..14857017.62 rows=25422025 width=8) (actual time=182921.056..291687.996 rows=26709939 loops=1)
                                 Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=108.49..108.49 rows=3349 width=1258) (actual time=2.256..2.256 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Seq Scan on area  (cost=0.00..108.49 rows=3349 width=1258) (actual time=0.007..0.666 rows=3349 loops=1)
 Total runtime: 301493.379 ms

インデックススキャン(〜3秒)explain.depesz.com

Unique  (cost=17352256.47..17353067.50 rows=200 width=1942) (actual time=3603.303..3681.619 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6284.60..619979.56 rows=332340 width=297) (actual time=26.201..262.314 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6201.51 rows=335354 width=0) (actual time=25.381..25.381 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=17352256.47..17352661.98 rows=162206 width=1942) (actual time=3603.302..3623.113 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=1296.08..17338219.59 rows=162206 width=1942) (actual time=29.980..3375.921 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Nested Loop  (cost=0.00..17334287.66 rows=162206 width=684) (actual time=26.903..3268.674 rows=155194 loops=1)
                     ->  CTE Scan on data  (cost=0.00..6646.80 rows=332340 width=676) (actual time=26.205..421.858 rows=335130 loops=1)
                     ->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) (actual time=0.006..0.008 rows=0 loops=335130)
                           Index Cond: (data_id = data.id)
                           Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=1254.22..1254.22 rows=3349 width=1258) (actual time=3.057..3.057 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Index Scan using area_primary_key on area  (cost=0.00..1254.22 rows=3349 width=1258) (actual time=0.012..1.429 rows=3349 loops=1)
 Total runtime: 3706.630 ms

テーブルの構造

これはテーブルのテーブル構造ですdata_area。必要に応じて、他のテーブルを提供できます。

CREATE TABLE data_area
(
  data_id integer NOT NULL,
  area_id integer NOT NULL,
  CONSTRAINT data_area_pkey PRIMARY KEY (data_id , area_id ),
  CONSTRAINT data_area_area_id_fk FOREIGN KEY (area_id)
      REFERENCES area (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT data_area_data_id_fk FOREIGN KEY (data_id)
      REFERENCES data (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
);

クエリ

WITH data AS (
    SELECT * 
    FROM data 
    WHERE 
        datasetid IN (1) 
        AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') 
        AND depth BETWEEN 0 AND 99999
)
SELECT * 
FROM ( 
    SELECT DISTINCT ON (data.id) data.id, * 
    FROM 
        data, 
        data_area 
        LEFT JOIN area ON area_id = area.id 
    WHERE 
        data_id = data.id 
        AND area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
) as s;

153812行を返します。DID set enable_seqscan= false;無効シーケンシャルスキャンに、インデックス結果を取得します。

私がやって試してみたANALYSEデータベースに、クエリで使用される列に収集された統計を増やすことが、何も助けていないようにみえます。

誰かがこれを広げてこれに光を当てるか、私が試すべき他のことを提案できますか?


これらの各実行プランを生成するクエリを含めると役立ちます
マイクシェリル「キャットリコール」、

推定される行数と実際の行数の2桁の違いは?私はそれを正しく読んでいますか?
マイクシェリル「キャットリコール」

@Catcallクエリを追加しました(何が起こっているのかを理解できるようにするための基本的な種類)。推定された行を参照すると、200と実際に153812が返されますか?
Mark Davidson

2
はい、200対150kは一見奇妙に見えます。左結合とデカルト積(FROM data, data_area)を混合する説得力のある理由はありますか?一見すると、ORDER BY句なしでDISTINCT ONを使用することは悪い考えのようです。
Mike Sherrill 'Cat Recall'

回答:


7

この行に注意してください:

->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) 
    (actual time=0.006..0.008 rows=0 loops=335130)

ループを考慮して総コストを計算すると、になります52.3 * 335130 = 17527299。これは、seq_scan代替案の14857017.62より大きいです。これが、インデックスを使用しない理由です。

したがって、オプティマイザはインデックススキャンのコストを過大評価しています。(クラスター化されたインデックスまたはそれがどのように読み込まれたかにより)インデックスでデータが並べ替えられているか、キャッシュメモリが十分にあるか、高速なディスクが十分にあると思います。したがって、ランダムなI / Oはほとんど行われていません。

また、インデックスコストを計算するときにオプティマイザーがクラスタリングを評価するために使用するcorrelationinを確認し、pg_stats最後にとを変更random_page_costしてcpu_index_tuple_cost、システムに一致させることをお勧めします。


私が何かを欠落していない限り、@ jopはを意味するの52.13ではなく52.3、17470326.9(まだseq_scanよりも大きい)になると思います
BotNet

2

あなたのCTEは実際には何も実行せず、いくつかのWHERE条件を「アウトソーシング」しWHERE TRUEます。それらのほとんどはと同等に見えます。CTEは通常、最適化フェンスの背後にある(つまり、それ自体で最適化される)ため、特定のクエリで非常に役立ちます。ただし、この場合は、まったく逆の効果が予想されます。

私が試そうとしていることは、クエリをできるだけ単純になるように書き換えることです。

SELECT d.id, * 
FROM 
    data d 
    JOIN data_area da ON da.data_id = d.id
    LEFT JOIN area a ON da.area_id = a.id 
WHERE 
    d.datasetid IN (1) 
    AND da.area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
    AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') -- this and the next condition don't do anything, I think
    AND depth BETWEEN 0 AND 99999
;

インデックスが使用されているかどうかを確認します。すべての出力列を必要としない可能性はまだあります(少なくとも、ジャンクションテーブルの2つの列は不要です)。

報告して、使用しているPostgreSQLのバージョンをお知らせください。


あなたの提案をありがとう、あなたの投稿への返信が遅れたことをお詫びします。私は他のプロジェクトに取り組んできました。あなたの提案は確かに、クエリがすべてのクエリに対して確実にインデックスを使用しているように見えることを意味しますが、それでも期待したパフォーマンスが得られません。多くのデータを含むクエリで分析を実行しました 。explain.depesz.com/ s / 1yuは4分程度かかり、95%の時間をINDEXスキャンに費やしています。
Mark Davidson

バージョン9.1.4を使用していることを忘れてしまった
マークデビッドソン

基本的にインデックススキャンは非常に高速ですが、問題は数百万回繰り返されることです。SET enable_nestloop=offクエリを実行する前に何を取得しますか?
dezso 2013

-1

フォロワーのために、私は次のような同様の問題を抱えていました

select * from table where bigint_column between x and y and mod(bigint_column, 10000) == z

問題は、bigint_columnの「xとyの間」にインデックスがありましたが、クエリは基本的にそのテーブルの「すべての行」だったため、インデックスを使用していませんでした(とにかくテーブル全体をスキャンする必要があったため)。 seq_scan順次スキャンを行っていました。私のための修正は、それはそれを使用できるようにすることを、方程式の「MOD」側のための新しいインデックスを作成することでした式に


ダウンボッターは理由についてコメントを残してお気軽にどうぞ:)
rogerdpack 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.