La actualización 2 (abajo) sugiere que el 14% de las geometrías de la tabla se almacenan fuera de línea con el resto de los registros de la tabla. Esto puede ser una pista útil.
El problema:
Tenemos una tabla con unos 170.000 polígonos WGS84, un índice geométrico GIST, y queremos obtener un conjunto de selección ordenado por valores de área de características descendentes. Como estos datos "nacieron" en ArcSDE, tienen un shape_area
campo.
Consulta con ORDER BY shape_area DESC
y LIMIT 2000
predicados, la intersección - utilizando &&
--tomó aproximadamente 25000 milisegundos (25 segundos) para terminar.
Así que ¿quizás la agrupación por área de polígono descendente?
Buscando más velocidad, probé a agrupar la tabla por un shape_area
índice como este:
CREATE INDEX shape_area_idx
ON layer_wgs84
USING btree
(shape_area DESC NULLS LAST)
WITH (FILLFACTOR=100) ;
ALTER TABLE layer_wgs84 CLUSTER ON shape_area_idx ;
cluster layer_wgs84 ;
-- then I dropped and recreated the geometry index
VACUUM ANALYZE layer_wgs84 ;
Después, pude correr la intersección sin el costoso ORDER BY
pero aún así tardó más de 3000 ms (3 segundos) en completarse.
..FWIW también he probado a usar un Consulta WITH (también conocida como expresión de tabla común) para asegurar que la intersección de BBox se procese primero. Pero esto no es realmente útil con un zoom bajo si el BBox cubre toda la tabla de 170.000 características. Incluso con el LIMIT
, sigue tardando entre 3 y 4 segundos.
Me gustaría reducirlo a unos 500 ms.
[ PREGUNTA REAL ]
Estratégicamente, ¿hay algún enfoque mejor que yo no conozca para realizar una intersección espacial ordenada por su área de forma descendente?
[ ACTUALIZACIÓN 1 ]
Cuando omito la geometría de la consulta, se convierte en algo estúpido. ¡50ms! Así que estoy pensando que mi consulta es tan buena como va a ser, y el cuello de botella debe ser la instancia de golpear el disco para obtener los objetos de geometría.
[ ACTUALIZACIÓN 2 ]
Porque tirar de las geometrías estaba matando la velocidad de retorno, Empecé a preguntarme si los objetos geométricos se almacenaban en línea o fuera de línea con el resto de los campos del registro y, en consecuencia, si podría considerar almacenar todas las geometrías en un campo diferente como VARCHAR(N)
o TEXT
en un esfuerzo por forzarlos en línea y evitar cualquier golpe de disco costoso. Buscando en Google esa pregunta me llevó a este hilo SO (cómo comprobar el modo de almacenamiento de cada columna de la tabla) y la página de documentación sobre TOAST (The Oversized-Attribute Storage Technique) .
Así que revisé esta tabla (en mi sistema de desarrollo) y la columna de geometría está configurada para "MAIN", lo que significa:
MAIN permite la compresión pero no el almacenamiento fuera de línea. (En realidad, el almacenamiento fuera de línea se realizará para estas columnas, pero sólo como último cuando no hay otra forma de hacer que la fila sea lo suficientemente pequeña para que quepa en una página).
Y este pasaje sugiere la implicación para los valores de la geometría en condiciones predeterminadas:
El código TOAST se activa sólo cuando un valor de fila que se va a almacenar en un tabla es mayor que TOAST_TUPLE_THRESHOLD bytes (normalmente 2 kB). El código código TOAST comprimirá y/o moverá los valores de los campos fuera de la línea hasta que que el valor de la fila sea más corto que TOAST_TUPLE_TARGET bytes (normalmente también 2 kB) o no se puedan obtener más ganancias.
Así queoooooo entonces me pregunté.. ¿Qué porcentaje de nuestra tabla se almacena fuera de línea?
RESPUESTA: aproximadamente 14%. (Incluyendo una parcela de la base militar que probablemente sea objeto de un número impactante de consultas). Para los curiosos, aquí he sondeado la base de datos para saber esto..
with
total as (
select count(*) from parcels_cama_wgs84 as count),
out_of_line as (
select count(*) from parcels_cama_wgs84 as count
where ST_mem_size(the_geom)*0.001 > 2)
select
total.count,
out_of_line.count,
(out_of_line.count::DECIMAL/total.count::DECIMAL)::DECIMAL as perc
from total, out_of_line;
| total | outofline | percent
| 164861 | 23193 | 0.14068
Lo que me llevó a preguntar.. ¿Qué pasa si modifico la consulta para que sólo devuelva las geometrías sin tostar?
Para ello, he modificado el WHERE
cláusula de mi selección interna (ver la consulta de abajo) para incluir esto..
AND ST_mem_size(the_geom)*0.001 < 2 -- only geoms less than 2kb
Después de eso mi consulta de caso de prueba pasó de 1400 ms a 850 ms..
Sin embargo. Aunque es un descubrimiento interesante, no aporta ninguna solución, además de que no podemos omitir todos los polígonos de más de 2kb de estas consultas de capas. ¡Así que aunque esto me dice algo, todavía estoy buscando el momento Aha!
Puedo forzar las geometrías en línea cambiando el modo de almacenamiento de las columnas a PLAIN
que trataré de comentar
[ Plan obligatorio de consultas y explicaciones | tl;dr; ]
Esta es mi consulta actual y su plan de explicación. Sin embargo, quiero subrayar que en realidad sólo estoy preguntando si hay un enfoque/táctica alternativa que debería considerar.
SELECT
l.gid,
st_astext(l.the_geom) AS geom,
l.tms AS tms,
l.shape_area_int
FROM (
SELECT * FROM
parcels_cama_wgs84
WHERE
the_geom && ST_Envelope(ST_GeogFromText('SRID=4326;POLYGON((-81.08030319213867 34.060588119230346, -81.08030319213867 34.09318398156763, -81.02404117584229 34.09318398156763, -81.02404117584229 34.060588119230346, -81.08030319213867 34.060588119230346))')::geometry)
) AS l
ORDER BY shape_area_int DESC
LIMIT 2000;
Explique el plan:
"Limit (cost=9970.86..9975.86 rows=2000 width=1155) (actual time=484.172..489.691 rows=2000 loops=1)"
" -> Sort (cost=9970.86..9977.31 rows=2580 width=1155) (actual time=484.169..489.564 rows=2000 loops=1)"
" Sort Key: parcels_cama_wgs84.shape_area_int"
" Sort Method: external merge Disk: 6640kB"
" -> Bitmap Heap Scan on parcels_cama_wgs84 (cost=136.32..8519.16 rows=2580 width=1155) (actual time=2.357..463.939 rows=2569 loops=1)"
" Recheck Cond: (the_geom && '0103000020E61000000100000005000000000000B0234554C06C4FFB59C1074140000000B0234554C009ACE473ED0B4140000000E4894154C009ACE473ED0B4140000000E4894154C06C4FFB59C1074140000000B0234554C06C4FFB59C1074140'::geometry)"
" -> Bitmap Index Scan on parcels_cama_wgs84_the_geom_geom_idx (cost=0.00..135.67 rows=2580 width=0) (actual time=2.066..2.066 rows=2569 loops=1)"
" Index Cond: (the_geom && '0103000020E61000000100000005000000000000B0234554C06C4FFB59C1074140000000B0234554C009ACE473ED0B4140000000E4894154C009ACE473ED0B4140000000E4894154C06C4FFB59C1074140000000B0234554C06C4FFB59C1074140'::geometry)"
"Total runtime: 491.461 ms"
0 votos
¿Has probado a ordenar por ST_Area(geom) las geometrías que pasan el test ST_Intersects?
1 votos
Por favor, eche un vistazo: wiki.postgresql.org/wiki/Slow_Query_Questions . Especialmente la parte de explicar debe ser útil.
0 votos
@JakubKania te pillé... es un buen enlace. Pero estoy más interesado en un enfoque diferente en conjunto en lugar de mejorar mi consulta existente. fwiw voy a adjuntar la pregunta con mi consulta actual y el plan de explicar ...sin embargo creo que sólo va a hacer que sea super ruidoso.
0 votos
@elrobis No se trata del ruido, se trata de entender lo que realmente está pasando. Deberías aumentar
WORK_MEM
probablemente también ajuste los búferes compartidos.0 votos
La cuestión es que no estoy pidiendo ayuda para diagnosticar el enfoque que utilicé (agrupación), sino si hay enfoques alternativos que no he considerado..
2 votos
Es posible que sea difícil crear un índice en conjuntos de datos en los que la densidad de los objetos está agrupada o es irregular en el espacio. En estos casos, las estrategias de indexación fractal como Q-Tree son más eficientes. Pero no puedo encontrar ningún esquema de indexación en Postgis para ese enfoque.
0 votos
@huckfinn, me he dado cuenta de que si omito las geometrías de la consulta, devuelve súper rápido como 50ms. Pero cuando las geometrías están presentes en la consulta, salta hasta appx 2800ms. Esto me hace pensar que mi consulta está bien, pero que de alguna manera el cuello de botella está golpeando el disco para las geometrías. hmm