PostGIS断続的なINDEXパフォーマンス


8

約5500万のデータポイント(ポイントはSRID 4326のジオメトリ)を含むテーブルがあり、クエリのために、これを大きなポリゴンからさまざまな範囲のさまざまな( 2000 km四方)からかなり小さい(小さいのは約100 km四方)。

ユーザーが選択した最初のクエリは、最初の5500万ポイントを、ユーザーが選択した日付範囲などに応じて約30万ポイントに絞り込みます。次に、結合が行われ、クエリが完了すると、使用するために選択したエリアセットに応じて、通常は最大150,000に絞り込まれます。

私が抱えている問題は、クエリがときどき停止するだけで、予想される〜25秒ではなく、最大〜18分かかる場合があることです。この時点で、通常、VACUUM ANALYZEを実行し、いくつかのクエリを実行してから、再び動作を開始する必要があります。この時点では、データまたはエリアテーブルのデータは追加、更新、または削除されていません。

私は考えることができるすべてのものをいじってみましたが、これはまだそれも不変で起こっているようです。data.point列とarea.polygon列の両方にGIST INDEXESがあります。

data.point列からINDEXを削除すると、状況が少し安定したように見えましたが、通常は35秒程度遅くなっています。しかし、INDEXを削除することは、妨げにならないようにするのに役立たないので、非常に悪い選択のようです。

PostGIS 1.5でPostgreSQL 9.1.4を使用しています

これが私が実行しているクエリです

    select * FROM data, area WHERE st_intersects (data.point, area.polygon) AND 
(readingdatetime BETWEEN '1948-01-01' AND '2012-11-19') AND datasetid IN(3) AND
 "polysetID" = 1 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)

説明する

Nested Loop  (cost=312.28..336.59 rows=5 width=2246) (actual time=1445.973..11557.824 rows=12723 loops=1)
  Join Filter: _st_intersects(data.point, area.polygon)
  ->  Index Scan using "area_polysetID_index" on area  (cost=0.00..20.04 rows=1 width=1949) (actual time=0.017..0.229 rows=35 loops=1)
        Index Cond: ("polysetID" = 1)
        Filter: (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[]))
  ->  Bitmap Heap Scan on data  (cost=312.28..316.29 rows=1 width=297) (actual time=328.771..329.136 rows=641 loops=35)
        Recheck Cond: ((point && area.polygon) AND (datasetid = 3))"
        Filter: ((readingdatetime >= '1948-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-11-19 00:00:00'::timestamp without time zone))
        ->  BitmapAnd  (cost=312.28..312.28 rows=1 width=0) (actual time=328.472..328.472 rows=0 loops=35)
              ->  Bitmap Index Scan on data_point_index  (cost=0.00..24.47 rows=276 width=0) (actual time=307.115..307.115 rows=1365770 loops=35)
                    Index Cond: (point && area.polygon)
              ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..284.37 rows=12856 width=0) (actual time=1.522..1.522 rows=19486 loops=35)
                    Index Cond: (datasetid = 3)
Total runtime: 11560.879 ms

私の作成テーブル

CREATE TABLE data
(
  id bigserial NOT NULL,
  datasetid integer NOT NULL,
  readingdatetime timestamp without time zone NOT NULL,
  value double precision NOT NULL,
  description character varying(255),
  point geometry,
  CONSTRAINT "DATAPRIMARYKEY" PRIMARY KEY (id ),
  CONSTRAINT enforce_dims_point CHECK (st_ndims(point) = 2),
  CONSTRAINT enforce_geotype_point CHECK (geometrytype(point) = 'POINT'::text OR point IS NULL),
  CONSTRAINT enforce_srid_point CHECK (st_srid(point) = 4326)
);

CREATE INDEX data_datasetid_index ON data USING btree (datasetid);
ALTER TABLE data CLUSTER ON data_datasetid_index;

CREATE INDEX "data_datasetid_readingDatetime_index" ON data USING btree (datasetid , readingdatetime );
CREATE INDEX data_point_index ON data USING gist (point);

CREATE INDEX "data_readingDatetime_index" ON data USING btree (readingdatetime );

CREATE TABLE area
(
  id serial NOT NULL,
  polygon geometry,
  "polysetID" integer NOT NULL,
  CONSTRAINT area_primary_key PRIMARY KEY (id )
)

CREATE INDEX area_polygon_index ON area USING gist (polygon);
CREATE INDEX "area_polysetID_index" ON area USING btree ("polysetID");
ALTER TABLE area CLUSTER ON "area_polysetID_index";

他に何かを知る必要がある場合、すべてがある程度の意味をなすことを願って、質問してください。

簡単にまとめると、INDEXESが機能する場合とそうでない場合があります。

誰かが私が何が起こっているのかを理解しようと試みることができる何かを提案できますか?

前もって感謝します。

編集:

もう一つの例

select * FROM data, area WHERE st_intersects ( data.point, area.polygon) AND 
(readingdatetime BETWEEN '2009-01-01' AND '2012-01-19') AND datasetid IN(1,3) AND
 "polysetID" = 1 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) 

ポイントインデックスを含むテーブルのコピーで実行

Nested Loop  (cost=0.00..1153.60 rows=35 width=2246) (actual time=86835.883..803363.979 rows=767 loops=1)
  Join Filter: _st_intersects(data.point, area.polygon)
  ->  Index Scan using "area_polysetID_index" on area  (cost=0.00..20.04 rows=1 width=1949) (actual time=0.021..16.287 rows=35 loops=1)
        Index Cond: ("polysetID" = 1)
        Filter: (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[]))
  ->  Index Scan using data_point_index on data  (cost=0.00..1133.30 rows=1 width=297) (actual time=17202.126..22952.706 rows=33 loops=35)
        Index Cond: (point && area.polygon)
        Filter: ((readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone) AND (datasetid = ANY ('{1,3}'::integer[])))
Total runtime: 803364.120 ms

ポイントインデックスのないテーブルのコピーで実行

Nested Loop  (cost=2576.91..284972.54 rows=34 width=2246) (actual time=181.478..235.608 rows=767 loops=1)
  Join Filter: ((data_new2.point && area.polygon) AND _st_intersects(data_new2.point, area.polygon))
  ->  Index Scan using "area_polysetID_index" on area  (cost=0.00..20.04 rows=1 width=1949) (actual time=0.149..0.196 rows=35 loops=1)
        Index Cond: ("polysetID" = 1)
        Filter: (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[]))
  ->  Bitmap Heap Scan on data_new2  (cost=2576.91..261072.36 rows=90972 width=297) (actual time=4.808..5.599 rows=2247 loops=35)
        Recheck Cond: ((datasetid = ANY ('{1,3}'::integer[])) AND (readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone))
        ->  Bitmap Index Scan on "data_new2_datasetid_readingDatetime_index"  (cost=0.00..2554.16 rows=90972 width=0) (actual time=4.605..4.605 rows=2247 loops=35)
              Index Cond: ((datasetid = ANY ('{1,3}'::integer[])) AND (readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone))
Total runtime: 235.723 ms

ご覧のとおり、ポイントインデックスが使用されている場合、クエリは大幅に遅くなります。

編集2(ポール推奨クエリ):

WITH polys AS (
  SELECT * FROM area
  WHERE "polysetID" = 1 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)
)
SELECT * 
FROM polys JOIN data ON ST_Intersects(data.point, polys.polygon)
WHERE datasetid IN(1,3) 
AND (readingdatetime BETWEEN '2009-01-01' AND '2012-01-19');

説明する

Nested Loop  (cost=20.04..1155.43 rows=1 width=899) (actual time=16691.374..279065.402 rows=767 loops=1)
  Join Filter: _st_intersects(data.point, polys.polygon)
  CTE polys
    ->  Index Scan using "area_polysetID_index" on area  (cost=0.00..20.04 rows=1 width=1949) (actual time=0.016..0.182 rows=35 loops=1)
          Index Cond: ("polysetID" = 1)
          Filter: (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[]))
  ->  CTE Scan on polys  (cost=0.00..0.02 rows=1 width=602) (actual time=0.020..0.358 rows=35 loops=1)
  ->  Index Scan using data_point_index on data  (cost=0.00..1135.11 rows=1 width=297) (actual time=6369.327..7973.201 rows=33 loops=35)
        Index Cond: (point && polys.polygon)
        Filter: ((datasetid = ANY ('{1,3}'::integer[])) AND (readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone))
Total runtime: 279065.540 ms

「クエリ」については、毎回まったく同じSQLであるかのように話します。それは...ですか?
Paul Ramsey、

1
ポールが上で言ったことに加えて、クエリのEXPLAINの結果を投稿するのはどうですか?postgresql.org/docs/8.1/static/sql-explain.html
Kelso

@PaulRamseyいいえ、クエリは毎回同じではありません。これは、ユーザーが選択した入力に依存するということです。ただし、それらのフィルターが何であるかを区別しないので、私はそれで私の質問を誤解しませんでした。それは、厳密な制約のために行が少ない場合に予想される方法でそれがより速くそしてその逆に影響するだけですその逆。ただし、クエリが何であっても、上で説明したVACCUM ANALYZEを実行するまで、実行が非常に遅くなる場合と同じように影響を受けます。
Mark Davidson

@ケルソ申し訳ありませんが、すぐにEXPLAINを追加することを忘れていました。
Mark Davidson

EXPLAINと他のいくつかのビットを追加しました。
Mark Davidson

回答:


4

効果的にプランナーにあなたが望むことをさせることは助けになるかもしれません。この場合、ポイントテーブルとの空間結合を実行するに、ポリゴンテーブルをサブセット化します。"WITH"構文を使用して、プランナーを裏切ることができる場合があります。

WITH polys AS (
  SELECT * FROM area
  WHERE area.id in 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)
)
SELECT * 
FROM polys JOIN data ON ST_Intersects(data.point, polys.polygon)
WHERE datasetid IN(3) 
AND (readingdatetime BETWEEN '1948-01-01' AND '2012-11-19');

これらのゲームをプレイする際の問題は、「私のポリゴンリストは常に他のクエリ部分よりも選択的である」という仮定をステートメントにコーディングしていることです。これは、クエリのすべてのパラメーター化、または異種分散データセットに対する特定のクエリのすべてのアプリケーションには当てはまらない場合があります。

しかし、うまくいくかもしれません。

更新:これは、句の選択性を事前に知っていると想定するという危険な道のりをさらに進んでいます。今回は、ポイントテーブルの属性選択も取り出し、空間結合の前に個別に実行します。

WITH polys AS (
  SELECT * FROM area
  WHERE area.id in 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)
),
WITH points AS (
  SELECT * FROM data
  WHERE datasetid IN(3) 
  AND (readingdatetime BETWEEN '1948-01-01' AND '2012-11-19')
)
SELECT * 
FROM polys JOIN points ON ST_Intersects(points, polys.polygon);

提案されたクエリとEXPLAIN結果のポールで私の質問を更新しました。パフォーマンスは向上しているように見えますが、それでも、インデックスがない場合に比べてどこにも劣っています。私はクエリを見ていて、readingdatetimeとデータセットを絞り込む前にすべてのポイントがポリゴン内にあるかどうかを調べようとしているのか、それともEXPLAINを誤解しているのでしょうか?
Mark Davidson

どのテーブルのreadingdatetimeとdatasetidが由来しているかを明確にできますか?
ポールラムジー

気にしないで、私はそれをDDLで見ます。問題はかなり明確になったと思います。それは、結合の選択性があまりにも選択的に戻ってしまうためです。他の条項がポイントテーブルをサブセット化していることに気づきませんでした。非空間フィルターをWITH句にプッシュします。
ポールラムジー

これは、残りの非空間クエリをWITH句にプッシュした後、非常に有望な問題に見えます。私は病気ので、その問題を解決することを100%確認するためにチャンスを持っていなかった最後の2日間の作業をオフしてきたが、私は賞に行くよ、あなたの両方ここと上のすべてのご助言など恵みのPostGIS-ユーザーリストは非常に役に立ちました。確実にわかったら、報告します。
Mark Davidson、

2

最初のEDITにポイントインデックスがあるテーブルのコピーで実行の説明を見ると、ポイントインデックスのないテーブルにこのインデックスがないことがわかります。

CREATE INDEX "data_readingDatetime_index" ON data USING btree (readingdatetime );

インデックスが存在することを確認できますか?

-編集-

あなたの質問を少し勉強した後(簡単な質問ではありません)、私は以下の提案をします。

  1. 「data_datasetid_readingDatetime_index」インデックスを削除します。すでに2つの列に個別にインデックスを付けているためです。これにより、スペースが節約され、挿入パフォーマンスが向上し、方程式から1つの変数を取り出すことでクエリプランナージョブが簡素化されます。
  2. テーブルをクラスター化し、「data_readingDatetime_index」インデックスを再度作成します。クラスタリングは、範囲ベースのクエリでより効果的です。範囲ベースの条件でデータセットIDをクエリしていないようです

    ALTER TABLE data CLUSTER ON data_readingDatetime_index;
  3. 実際のクラスタリングを実行します。前の項目のコマンドはテーブルをクラスター化せず、テーブルをクラスター化する場合、そのインデックスでクラスター化したいという希望を単に表現します。それをクラスター化する:

     CLUSTER data;
  4. テーブルをクラスター化した後、テーブルを分析して、統計情報(プランナーが選択する戦略を決定するために使用)がディスク上の新しいレイアウトを記録するようにします。

     VACUUM ANALYZE data;

    現在、readingdatetimeに対してデータが編成されているため、プランナはdata_readingDatetime_indexインデックスが使用される戦略を優先します。また、それが使用されるときは常に、Explainプランが最も高速であると思われるため、パフォーマンスの向上と変動は少なくなります。

上記のPaulの回答で述べたように、プランナーがフィルターに応じて戦略を変更しないとは思わないでください(フィルターが常に同じで、値のみが変更されている場合でも)。

強く推奨されているPostregSQL 9.0 High Performanceブックに、条件をselect ... from table tから変更する例があり、v <5からv <6はプランをインデックススキャンからフルテーブルスキャンに切り替えました。


彼の3番目の説明を見ると、「-> Bitmap Index Scan on "data_new2_datasetid_readingDatetime_index"(cost = 0.00..2554.16 rows = 90972 width = 0)(actual time = 4.605..4.605 rows = 2247 loops = 35 ) "これは、空間インデックスが方程式から取り出されると、その列のインデックスが実際に機能する場所です。
ポールラムジー

まさに私のポイント。遅いクエリではなく、高速なクエリにあります(遅いものは、3番目のクエリの前にある説明プランのクエリです)。テーブルをコピーするときにインデックスが失われたのでしょうか?
ユニコレッティ

いいえ、上記のように、空間インデックスは非常に高い選択性をプランナに(誤って)レポートしているため、プランナは空間インデックスを優先してそのインデックスをスキップしています。したがって、存在し、使用されていません。
ポールラムジー

@unicolettiあなたの入力は確かにあなたに提案を行ってくれて、すぐに結果について報告します。ありがとうございました。私は実際にPostgreSQL 9.0 High Performanceブックを完全に持っていますが、できる限り小さなパフォーマンスブーストを確実に得るには、自分でもっと読む必要があることを強くお勧めします。
Mark Davidson、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.