5 votos

PostGIS: Estrategia para una selección más rápida por BBox con una cláusula ORDER BY área del polígono. ¿Cluster?

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.

1voto

waney Puntos 111

¿es el campo de área un flotante? puede intentar crear el índice de área (agrupado o no) en un campo entero (si su caso de uso puede aceptar la generalización).

0 votos

Perceptivo. Y lo es. Interesante idea, la probaré.

1 votos

Basándome en tu idea, volví a agrupar la tabla en el índice GIST/geom, añadí un índice como CEIL(shape_area) DESC y lo ejecuté con la bbox/intersección en un nested select ..lo hizo mejor a 2300 ms (ahorrando 700ms). Sigo esperando que haya una forma inteligente de bajarlo a una consulta de 500ms.

1voto

Hubris Puntos 405

Sospecho que geometrías como la de su área militar, que es grande e irregular y que, por tanto, tiene un bbox grande que se solapa con muchos otros, fuerza el paso de refinar (cf., filtrar-refinar) en la búsqueda de la coincidencia exacta demasiadas veces ralentizando el resultado. La velocidad no sólo se ve afectada por el tamaño, sino también por la complejidad de la geometría. Así que tal vez probar una generalización en sus geometrías ayudaría? En la mayoría de los casos, no es necesario hacer la consulta que se discute en las geometrías de detalle completo. Los valores de área no se verán afectados, ya que no se calculan en las geometrías reales, sino que se almacenan. Para asegurarse de que la topología de los polígonos es correcta, podría utilizar la simplificación de conservación de la topología de Postgis ( http://strk.keybit.net/blog/tag/postgis/ ) aunque tengo que advertir que el suyo puede llevar a resultados incorrectos en algunos casos (puede introducir intersecciones espurias introduciendo así una relación entre dos geometrías previamente disjuntas en algunos casos marginales de geomorfología no convexa, como las ensenadas profundas con una península saliente entrando en ella - probado en el puerto de Sydney hace algún tiempo).

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X