Tengo una tabla que contiene aproximadamente 55 millones de puntos de datos (el punto es una geometría con SRID 4326) y para mi consulta necesito unir esto a una tabla de área (actualmente tiene ~ 1800 áreas) que contiene una variedad de diferentes que van desde polígonos grandes ( 2000 km cuadrados) a bastante pequeño (pequeño siendo unos 100 km cuadrados).
La consulta inicial seleccionada por el usuario reduce los 55 millones de puntos iniciales a alrededor de ~ 300,000 puntos dependiendo del rango de fechas, etc. que seleccionen. Luego, la unión se realiza y depende del conjunto de áreas que hayan seleccionado para usar una vez que se complete la consulta, esto normalmente lo reduce a ~ 150,000.
El problema que tengo es que algunas veces la consulta simplemente se detiene y, en lugar de tomar los ~ 25 segundos esperados, puede tomar hasta ~ 18 minutos. En este punto, generalmente tiene que hacer un ANÁLISIS DE VACÍO y luego ejecutar algunas consultas antes de que comience a comportarse nuevamente. No se han agregado, actualizado o eliminado datos de las tablas de datos o áreas en este momento.
He jugado con todo lo que puedo pensar y esto todavía parece seguir sucediendo sin constancia. Tanto la columna data.point como la columna area.polygon tienen ÍNDICES GIST en ellas.
Descubrí que eliminar el ÍNDICE de la columna data.point parecía hacer las cosas un poco más estables, pero normalmente es más lento ~ 35 segundos normalmente. Sin embargo, eliminar un ÍNDICE parece ser una muy mala elección, ya que ¿no debería ayudar a no obstaculizar?
Estoy usando PostgreSQL 9.1.4 con PostGIS 1.5
Aquí está la consulta que estoy ejecutando
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)
EXPLIQUE
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
Mis tablas de creación
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";
Espero que todo tenga sentido si necesita saber algo más, por favor pregunte.
Un breve resumen es realmente que los ÍNDICES parecen funcionar algunas veces pero no otras.
¿Alguien podría sugerir algo que yo pueda tratar de averiguar qué está pasando?
Gracias por adelantado.
EDITAR:
Otro ejemplo
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)
Ejecutar en copia de tabla con índice de puntos
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
Ejecutar en copia de tabla sin índice de puntos
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
Como puede ver, la consulta es significativamente más lenta cuando se utiliza el índice de puntos.
EDITAR 2 (Consulta sugerida por Pauls):
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');
EXPLIQUE
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