Ich habe eine Tabelle mit ungefähr 55 Millionen Datenpunkten (Punkt ist eine Geometrie mit SRID 4326) und für meine Abfrage muss ich diese mit einer Flächentabelle (derzeit ~ 1800 Bereiche) verknüpfen, die eine Vielzahl verschiedener Bereiche enthält, die von großen Polygonen reichen ( 2000 km²) bis ziemlich klein (klein ca. 100 km²).
Durch die vom Benutzer ausgewählte anfängliche Abfrage werden die anfänglichen 55 Millionen Punkte auf etwa 300.000 Punkte eingegrenzt, abhängig vom ausgewählten Datumsbereich usw. Dann ist die Verknüpfung abgeschlossen und hängt davon ab, welchen Bereichssatz sie nach Abschluss der Abfrage ausgewählt haben. Dadurch wird sie normalerweise auf ~ 150.000 eingegrenzt.
Das Problem, das ich habe, ist, dass die Abfrage manchmal nur zum Stillstand kommt und anstatt der erwarteten ~ 25 Sekunden bis zu ~ 18 Minuten dauern kann. Zu diesem Zeitpunkt müssen Sie normalerweise eine VAKUUMANALYSE durchführen und dann einige Abfragen ausführen, bevor sie sich wieder verhalten. Zu diesem Zeitpunkt wurden keine Daten hinzugefügt, aktualisiert oder aus den Daten- oder Bereichstabellen entfernt.
Ich habe mit allem herumgespielt, was mir einfällt, und dies scheint immer noch ohne Konstanz zu geschehen. Sowohl die data.point-Spalte als auch die area.polygon-Spalte enthalten GIST INDEXES.
Ich fand, dass das Entfernen des INDEX aus der Spalte data.point die Dinge etwas stabiler zu machen schien, aber normalerweise ~ 35 Sekunden langsamer ist. Das Entfernen eines INDEX scheint jedoch eine sehr schlechte Wahl zu sein, da es nicht helfen sollte, nicht zu behindern?
Ich verwende PostgreSQL 9.1.4 mit PostGIS 1.5
Hier ist die Abfrage, die ich ausführe
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)
ERKLÄREN
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
Meine Tabellen erstellen
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";
Hoffe, dass alles einen gewissen Sinn ergibt, wenn Sie etwas anderes wissen müssen, fragen Sie bitte.
Eine kurze Zusammenfassung ist wirklich, dass die INDEXES einige Male zu funktionieren scheinen, andere jedoch nicht.
Könnte jemand etwas vorschlagen, was ich versuchen könnte, um herauszufinden, was passiert?
Danke im Voraus.
BEARBEITEN:
Ein anderes Beispiel
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)
Führen Sie eine Kopie der Tabelle mit Punktindex aus
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
Führen Sie eine Kopie der Tabelle ohne Punktindex aus
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
Wie Sie sehen, ist die Abfrage erheblich langsamer, wenn der Punktindex verwendet wird.
EDIT 2 (Pauls vorgeschlagene Abfrage):
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');
ERKLÄREN
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
quelle
Antworten:
Es könnte hilfreich sein, den Planer effektiv zu zwingen, das zu tun, was Sie wollen. In diesem Fall wird die Polygontabelle vor dem Ausführen der räumlichen Verknüpfung mit der Punktetabelle untergeordnet. Möglicherweise können Sie den Planer mithilfe der "WITH" -Syntax überlisten:
Das Problem beim Versuch, diese Spiele zu spielen, besteht darin, dass Sie die Annahme "Meine Polygonliste ist immer selektiver als meine anderen Abfrageteile" in Ihre Aussage eintragen. Dies gilt möglicherweise nicht für alle Parametrisierungen Ihrer Abfrage oder für alle Anwendungen einer bestimmten Abfrage über ein heterogen verteiltes Dataset.
Aber es könnte funktionieren.
UPDATE : Dies geht noch weiter, wenn Sie davon ausgehen, dass Sie die Selektivität Ihrer Klauseln im Voraus kennen. Dieses Mal nehmen wir auch die Attributauswahl in der Punktetabelle heraus und führen sie separat vor dem räumlichen Join aus:
quelle
Wenn Sie sich die Erklärung für die Kopie der Tabelle mit Punktindex bei der ersten Bearbeitung ansehen, sehen Sie, dass Sie diesen Index für die Tabelle ohne Punktindex vermissen:
Können Sie bestätigen, dass der Index vorhanden ist?
- BEARBEITEN -
Nach einigem weiteren Studium Ihrer Frage (übrigens keine einfache) habe ich die folgenden Vorschläge zu machen.
Cluster die Tabelle gegen den Index "data_readingDatetime_index". Clustering ist bei bereichsbasierten Abfragen effektiver. Sie scheinen keine Datensatz-ID mit bereichsbezogenen Bedingungen abzufragen
Führen Sie das eigentliche Clustering durch. Der Befehl im vorherigen Element gruppiert Ihre Tabelle nicht, sondern drückt lediglich Ihren Wunsch aus, dass die Tabelle, wenn sie geclustert werden soll, in diesem Index geclustert werden soll. Cluster es mit:
Analysieren Sie die Tabelle nach dem Clustering, damit die Statistiken (anhand derer der Planer entscheidet, welche Strategie er wählen soll) das neue Layout auf der Festplatte notieren:
Da die Daten jetzt gegen die Lesezeit organisiert sind, wird der Planer eine Strategie bevorzugen, bei der der Index data_readingDatetime_index verwendet wird. Da der EXPLAIN-Plan bei jeder Verwendung am schnellsten zu sein scheint, verbessert sich die Leistung möglicherweise und schwankt möglicherweise weniger
Denken Sie nicht, dass der Planer die Strategie in Abhängigkeit von den Filtern nicht ändern wird (auch wenn die Filter immer gleich sind und sich nur ihre Werte ändern).
Es gibt ein Beispiel in dem sehr empfohlenen PostregSQL 9.0-Hochleistungsbuch, in dem das Ändern einer Bedingung von select ... von table t, wobei v <5 bis v <6 den Plan vom Index-Scan zum vollständigen Table-Scan umschaltete.
quelle