¿Cómo hacer uso de índices con unión interna en Postgis?

8

Obtuve 2 conjuntos de puntos en 2 tablas separadas. Table_a obtuvo 100k puntos y table_b obtuvo 300k de puntos. Intento encontrar los puntos más cercanos en relación, encuéntrame cualquier punto de table_b que esté dentro de los 50 metros de tabla_a. Después de calcular la columna de caída, agrúpelos por table_a a_id columna y devuelva el valor más alto.

Escribí una siguiente consulta que cumple con este criterio

SELECT DISTINCT ON (a_id) *
FROM (
       SELECT
         table_b.b_id,
         table_b.height - st_3ddistance(table_b.geom, table_a.geom) fall,
         table_b.geom,
         table_a.a_id
       FROM table_a
         INNER JOIN table_b ON _st_3ddwithin(table_a.geom, table_b.geom, 50)) a
WHERE fall >= 0
ORDER BY a_id, fall DESC;

Agregué índices de geometría 3d:

CREATE INDEX table_a_geom ON table_a USING GIST (geom gist_geometry_ops_nd);
CREATE INDEX table_b_geom ON table_b USING GIST (geom gist_geometry_ops_nd);

Sin embargo, mi problema es que no puedo hacer una consulta para usarlos. El planeador de consultas es seguir eligiendo la exploración de secuencia que es lenta. Ejecuto algunas pruebas cambiando _st_3ddwithin con st_3ddwithin , <<->> <50 , creando 50 m de búfer e intersección , st_3ddistance <50 pero cada vez que el planificador elige la exploración de secuencia. ¿Hay alguna manera de usar índices con mayor rendimiento o cambiar la consulta para usar índices?

Mi plan de consulta:

Unique  (cost=10462593.70..10473018.43 rows=1 width=144)
  ->  Sort  (cost=10462593.70..10467806.06 rows=2084945 width=144)
        Sort Key: table_a.nmbayuid, ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom))) DESC
        ->  Nested Loop  (cost=0.00..10243762.28 rows=2084945 width=144)
              Join Filter: (_st_dwithin(table_a.geom, table_b.geomgr, '50'::double precision) AND ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom)) >= '0'::double precision))
              ->  Seq Scan on table_b  (cost=0.00..1459.47 rows=47147 width=96)
              ->  Materialize  (cost=0.00..10.97 rows=398 width=56)
                    ->  Seq Scan on table_a  (cost=0.00..8.98 rows=398 width=56)
Losbaltica
fuente
1
¿Qué es exactamente e_wires_mv12404 que está en el plan de consulta pero no el SQL? ¿Cómo se ve el plan de consulta solo para la consulta interna? Sugiero no usar la función que comienza con _ST. Finalmente, puede obtener un mejor rendimiento con ST_DWithin en 2D, con 35 metros, que es más o menos lo mismo que 50 metros de los bordes opuestos de un cubo. Como está buscando el punto más cercano dentro de los 50 metros, este podría ser un buen candidato para una unión lateral y usar la construcción ORDER BY a.geom <-> b.geom.
John Powell el
1
Tuve un problema similar el año pasado, desenterré esta publicación para ti , avísame si no responde tus preguntas.
WxGeo
2
Si observa la definición sql de las funciones, verá que las funciones st_ como st_dwithin en realidad son una casilla de verificación de límite y una llamada a la función st . Es la parte del cuadro delimitador que puede usar el índice cuando llama directamente a la función st. No hay forma de que la base de datos use el índice. Llama a la función de verificación nuevamente.
Nicklas Avén
1
¿Desea que escriba la solución de unión lateral? Creo que funcionaría bien para lo que usted describe
John Powell
1
Las funciones de @AndreSilva que comienzan con _STson funciones internas llamadas por PostGIS después de filtrar con un índice. Si los llama directamente, no se usará el índice.
dbaston

Respuestas:

6

En primer lugar, como se ha señalado en los comentarios, el guión bajo principal antes de la función ST, es decir, _ST_3DWithin hará que el índice no se utilice. No puedo encontrar ninguna mención reciente de esto, pero en documentos más antiguos si busca, por ejemplo, _ST_Intersects, dice:

Para evitar el uso del índice, use la función _ST_Intersects.

EDITAR: según lo aclarado por @dbaston en los comentarios, las funciones con el guión bajo principal son funciones internas que no usan el índice cuando se llama y este sigue siendo el caso (aunque es difícil de encontrar en los documentos).

Su consulta posiblemente podría beneficiarse de la sintaxis LATERAL JOIN, que se presta bien para problemas con el vecino más cercano (kNN) como este.

SELECT 
   a.a_id, 
   b.b_id
   b.height - ST_3Ddistance(b.geom, a.geom) AS fall,
  FROM table_a a
     LEFT JOIN LATERAL
       (SELECT
            b_id,         
            geom,
            height        
          FROM table_b
          WHERE ST_3Ddwithin(a.geom, geom, 50)
          AND height - ST_3Ddistance(geom, a.geom) > 0
          ORDER BY height - ST_3Ddistance(b.geom, a.geom) DESC 
          LIMIT 1
        ) b ON TRUE;

Esto le permite encontrar las geometrías k más cercanas de la tabla a (en este caso 1, debido al LÍMITE 1) a la tabla b, ordenadas por la distancia 3D entre ellas. Está escrito usando una IZQUIERDA IZQUIERDA, ya que es posible que haya algunas geometrías en la tabla a que no estén dentro de los 50 metros de la tabla b.

Las consultas laterales le permiten hacer referencia a columnas de la cláusula FROM anterior, lo que lo hace más poderoso que las subconsultas estándar, consulte los documentos .

No puedo probar esto con sus datos, pero cuando ejecuté consultas similares, la instrucción EXPLAIN indica el uso adecuado del índice.

John Powell
fuente
Sus comentarios tienen mucho sentido, pero no puedo aceptar la respuesta porque la consulta que proporcionó está haciendo diferente a la consulta original. Como mansión antes "no busco el punto más cercano sino un grupo de puntos dentro de los 50 metros y luego selecciono uno con el valor de resta más alto (altura - ST_3Ddistance (geom, a.geom)) agrupado por a_id
Losbaltica
Modifiqué
1
Modifiqué la consulta, lo único que faltaba era "altura -" en el orden de. Esto ahora encontrará todos los puntos dentro de 50 y devolverá el que tenga la altura más alta: valor ST_3Ddistance (b.geom, a.geom). No hay necesidad de diferenciar, ya que todo esto se maneja con cada consulta lateral y LÍMITE 1, es decir, solo obtendrá el mayor valor de caída para cada a_id.
John Powell
¿Funciona ahora como esperaba originalmente? ¿EXPLICAR parece razonable?
John Powell
Funciona como se esperaba. El rendimiento de la consulta es casi el mismo, pero el costo de la consulta es mucho menor. Nuevo EXPLICAR: explica.depesz.com/s/Js5G Creo que alcanzo el límite de optimización de consultas y solo creo que lo que puedo hacer ahora es ajustar el servidor o refactorizar las tablas / lógica. Me responde la pregunta original
Losbaltica
2

Este enlace a la documentación de PostGIS recomienda los siguientes pasos para garantizar que los índices y el planificador de consultas estén optimizados:

  1. Asegúrese de que se recopilen estadísticas sobre el número y la distribución de valores en una tabla, para proporcionar al planificador de consultas una mejor información para tomar decisiones sobre el uso del índice. ANÁLISIS DE VACÍO calculará ambos.

  2. Si pasar la aspiradora no ayuda, puede forzar temporalmente al planificador a usar la información del índice utilizando el set enable_seqscan en off; mando. De esta manera, puede verificar si el planificador es capaz de generar un plan de consulta acelerado de índice para su consulta. Solo debe usar este comando solo para la depuración: en general, el planificador sabe mejor que usted sobre cuándo usar índices. Una vez que haya ejecutado su consulta, no olvide volver a configurar ENABLE_SEQSCAN para que otras consultas utilicen el planificador de manera normal.

  3. Si se establece enable_seqscan en off; ayuda a ejecutar su consulta, es probable que su Postgres no esté ajustado para su hardware. Si encuentra que el planificador está equivocado sobre el costo de los escaneos secuenciales vs de índice, intente reducir el valor de random_page_cost en postgresql.conf o use set random_page_cost a 1.1 ;. El valor predeterminado para el parámetro es 4, intente configurarlo en 1 (en SSD) o 2 (en discos magnéticos rápidos). Disminuir el valor hace que el planificador esté más inclinado a usar escaneos de índice.

  4. Si se establece enable_seqscan en off; no ayuda a su consulta, puede suceder que use una construcción que Postgres aún no puede desenredar. Una subconsulta con selección en línea es un ejemplo: debe volver a escribirla en el planificador de formularios para optimizar, por ejemplo, una UNIÓN LATERAL.

Por lo tanto, primero intente los pasos 1-3 antes de volver a escribir su consulta para usar los índices. Si eso no funciona, puede intentar modificar la consulta.

Creo (a lo mejor de mi capacidad para crear SQL sin ejecutar el código) que la consulta a continuación devolverá resultados idénticos a los suyos, pero no sé si será más eficiente.

SELECT DISTINCT on (a_id),
    table_b.b_id as b_id,
    table_b.height - st_3ddistance(table_b.geom, table_a.geom) as fall,
    table_b.geom as b_geom,
    table_a.a_id as a_id
    FROM table_a
         INNER JOIN table_b ON _st_3ddwithin(table_a.geom, table_b.geom, 50)) a
WHERE fall >= 0
ORDER BY a_id, fall DESC;
jgm_GIS
fuente
Muy interesante después de cambiar _st_3ddwithin a st_dwithin como otros comentarios sugeridos y ejecutar VACUUM ANALYZE después, ¡el planificador finalmente comienza a captar el índice!
Losbaltica
0

Si está utilizando Postgres 10 (o más reciente), le recomiendo cargar sus datos en tablas paralelas.

Probablemente necesitará pasar tiempo ajustándolo (particionamiento de datos y la cantidad de trabajadores), pero creo que vale la pena el esfuerzo. Teóricamente, KNN es altamente paralelizable, alcanzando complejidades de tiempo constantes, incluso O (1) si la cantidad de trabajadores es igual al número de elementos donde se calculará una operación KNN.

Aquí se proporciona alguna referencia práctica sobre cómo cargar los datos y realizar las consultas . Proporciona algunos detalles sobre el ajuste del plan (para obligar a más trabajadores a ser intervenidos) aquí . Es importante señalar que las secuencias de comandos paralelas implican una gran cantidad de coordinación de tareas, por lo que el límite teórico extremo de proporcionar la paralelización más extrema no se cumple en la práctica, debido a la creación de redes y otras características de diseño de sistemas.

Ricardo Barros Lourenço
fuente