Estoy tratando de mejorar el rendimiento de la consulta a continuación. No importa cómo escriba la consulta (subconsulta en la cláusula FROM, subconsulta en la cláusula WHERE) postgres insiste en ejecutar todas las ~ 570K filas a través de la costosa función ST_DWITHIN, aunque solo hay 60 filas donde county = 24. ¿Cómo puedo hacer que Postgres filtre en condado = 24 ANTES de ejecutar el postgis func que me parece que sería mucho más rápido y mucho más eficiente? 700 ms no es motivo de demasiada preocupación, pero a medida que esta tabla crece a 10M +, me preocupa el rendimiento.
También para tener en cuenta, p.id es una clave primaria, p.zipcode es un índice fk, z.county es un índice fk y p.geom tiene un índice GiST.
Consulta:
EXPLAIN ANALYZE
SELECT count(p.id)
FROM point AS p
LEFT JOIN zipcode AS z
ON p.zipcode = z.zipcode
WHERE z.county = 24
AND ST_DWithin(
p.geom,
ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269),
16090.0,
false
)
EXPLIQUE ANALIZAR:
Aggregate (cost=250851.91..250851.92 rows=1 width=4) (actual time=724.007..724.007 rows=1 loops=1)
-> Hash Join (cost=152.05..250851.34 rows=228 width=4) (actual time=0.359..723.996 rows=51 loops=1)
Hash Cond: ((p.zipcode)::text = (z.zipcode)::text)
-> Seq Scan on point p (cost=0.00..250669.12 rows=7437 width=10) (actual time=0.258..723.867 rows=63 loops=1)
Filter: (((geom)::geography && '0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography) AND ('0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography && _st_expand((geom)::geography, 16090::double precision)) AND _st_dwithin((g (...)
Rows Removed by Filter: 557731
-> Hash (cost=151.38..151.38 rows=54 width=6) (actual time=0.095..0.095 rows=54 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 3kB
-> Bitmap Heap Scan on zipcode z (cost=4.70..151.38 rows=54 width=6) (actual time=0.023..0.079 rows=54 loops=1)
Recheck Cond: (county = 24)
Heap Blocks: exact=39
-> Bitmap Index Scan on fki_zipcode_county_foreign_key (cost=0.00..4.68 rows=54 width=0) (actual time=0.016..0.016 rows=54 loops=1)
Index Cond: (county = 24)
Planning time: 0.504 ms
Execution time: 724.064 ms
point
filas donde county = 24 en una nueva tabla por sí mismas, la consulta solo toma .453 ms en comparación con 724, por lo que definitivamente hay una gran diferencia.count(*)
como una cuestión de estilo. Siid
es un pkid como dices, es loNOT NULL
que significa que son lo mismo. Exceptocount(id)
tiene el inconveniente de que tiene que hacer esa pregunta siid
es anulable.Respuestas:
Puede ver el problema con los recuentos de filas esperados vs reales. El planificador piensa que hay 7.437 filas, sin embargo, solo hay 63. Las estadísticas están apagadas. Curiosamente, no está utilizando una búsqueda de índice (índice) de cuadro delimitador con el que
DWithin
puede pegar el resultado\d point
. ¿Qué versión de PostGIS y PostgreSQL?Intenta correr
ANALYZE point
. ¿Obtiene el mismo plan cuando sube la condición?fuente
Como nota al margen, existe una posibilidad razonable de que este comportamiento se modifique en PostGIS 2.3.0 si desea llamarlo un error.
De los documentos en PostgreSQL
Entonces, el costo predeterminado era 1 (muy barato).
D_Within
Usar un índice GIST es muy barato. Pero, eso se incrementó a 100 (por proxy de lo interno_ST_DWithin
).Yo tampoco soy un gran admirador del método CTE. Los CTE son una valla de optimización. Por lo tanto, hacer esto de esta manera elimina un margen potencial para la optimización futura. Si los valores predeterminados correctos lo arreglan, prefiero actualizar. Al final del día, tenemos que hacer el trabajo y ese método claramente funciona para usted.
fuente
Gracias a la sugerencia de John Powell, revisé la consulta para poner la condición de limitación del condado en una consulta con / CTE y esto mejoró bastante el rendimiento a 222 ms frente a 700. Todavía estoy muy lejos de los .74 ms que obtengo cuando los datos están en su Mesa propia. Todavía no estoy seguro de por qué el planificador no limita el conjunto de datos antes de ejecutar una costosa función postgis, y tendré que probar con conjuntos de datos más grandes cuando los tenga, pero esto parece ser una solución a esta situación única por ahora.
fuente
Debe crear un índice en
zipcode(county, zipcode)
, que debería darle un escaneo de índice solo en z.También es posible que desee experimentar con
btree_gist
la extensión ya sea la creación depoint(zipcode, geom)
índice opoint(geom, zipcode)
ezipcode(zipcode, county)
índice.fuente