¿Por qué st_intersects es más rápido que &&?

10

Es una tabla de puntos. ~ 1M de registros

SELECT COUNT(*) as value FROM alasarr_social_mv s; 
Output: 976270

Parece que st_intersects obliga a usar los índices espaciales, pero && no lo hace.

Muestra usando ST_Intersects(282ms)

SELECT COUNT(*) as value
FROM alasarr_social_mv 
WHERE ST_Intersects(
  the_geom_webmercator, 
  ST_MakeEnvelope(-410961,4920492,-402305,4926887,3857)
)


Aggregate  (cost=34370.18..34370.19 rows=1 width=0) (actual time=282.715..282.715 rows=1 loops=1)
  ->  Bitmap Heap Scan on alasarr_social_mv s  (cost=5572.17..34339.84 rows=60683 width=0) (actual time=21.574..240.195 rows=178010 loops=1)
        Recheck Cond: (the_geom_webmercator && '0103000020110F0000010000000500000000000000441519C1000000002BC5524100000000441519C1000000C069CB524100000000048E18C1000000C069CB524100000000048E18C1000000002BC5524100000000441519C1000000002BC55241'::geometry)
        Filter: _st_intersects(the_geom_webmercator, '0103000020110F0000010000000500000000000000441519C1000000002BC5524100000000441519C1000000C069CB524100000000048E18C1000000C069CB524100000000048E18C1000000002BC5524100000000441519C1000000002BC55241'::geometry)
        Heap Blocks: exact=4848
        ->  Bitmap Index Scan on alasarr_social_mv_gix  (cost=0.00..5569.13 rows=182050 width=0) (actual time=20.836..20.836 rows=178010 loops=1)
              Index Cond: (the_geom_webmercator && '0103000020110F0000010000000500000000000000441519C1000000002BC5524100000000441519C1000000C069CB524100000000048E18C1000000C069CB524100000000048E18C1000000002BC5524100000000441519C1000000002BC55241'::geometry)
Planning time: 0.192 ms
Execution time: 282.758 ms

Muestra usando &&(414ms)

SELECT COUNT(*) as value
FROM alasarr_social_mv  
WHERE the_geom_webmercator && 
  ST_MakeEnvelope(-410961,4920492,-402305,4926887,3857)

Aggregate  (cost=22535.97..22535.97 rows=1 width=0) (actual time=414.314..414.314 rows=1 loops=1)
  ->  Seq Scan on alasarr_social_mv  (cost=0.00..22444.94 rows=182050 width=0) (actual time=0.017..378.427 rows=178010 loops=1)
        Filter: (the_geom_webmercator && '0103000020110F0000010000000500000000000000441519C1000000002BC5524100000000441519C1000000C069CB524100000000048E18C1000000C069CB524100000000048E18C1000000002BC5524100000000441519C1000000002BC55241'::geometry)
        Rows Removed by Filter: 798260
Planning time: 0.134 ms
Execution time: 414.343 ms

Versión PostGIS

POSTGIS="2.2.2" GEOS="3.5.0-CAPI-1.9.0 r4084" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.11.0, released 2014/04/16" LIBXML="2.7.8" LIBJSON="UNKNOWN" (core procs from "2.2.2" need upgrade) RASTER (raster procs from "2.2.2" need upgrade)  alasarr 2 mins ago
alasarr
fuente
2
No pegue capturas de pantalla de texto en la pregunta. ¿Puedes copiar y pegar esos como código? No puedo leerlos para ayudarte.
Evan Carroll
Hecho. Creo que es un poco mejor ahora
alasarr
Traté de respaldar sus nuevos planes de consulta en eso. Siéntase libre de actualizar los planes, pero trate de mantener el estilo.
Evan Carroll
44
Echa un vistazo a esta pregunta . El operador && realmente realiza una consulta de cuadro delimitador, mientras que ST_Intersects utiliza una consulta de cuadro delimitador para determinar qué geometrías probar para la comparación real, por lo que esperaría que && sea más rápido. Sin embargo, es probable que el uso de ST_MakeEnvelope a la derecha de && esté haciendo que el planificador de consultas elija un escaneo completo de la tabla por alguna razón (como se desprende de la explicación). Intente crear primero la geometría, en un CTE, y vea si puede "engañar" al optimizador.
John Powell el
¡Tienes razón! funciona dentro de un CTE
alasarr

Respuestas:

16

Este tipo de hallazgo aparece con bastante frecuencia, y es un poco oscuro, por lo que vale la pena repetirlo. Si define una geometría dentro de una función que la usa, como ST_Intersects o && (que ST_Intersects usa debajo del capó), entonces el planificador de consultas elige un escaneo completo de la tabla, ya que "no" tiene conocimiento del resultado de la creación de la geometría función, es decir, ST_MakeEnvelope en este caso. Si define la geometría que desea verificar para la intersección en un CTE, entonces el optimizador se ocupa de una cantidad conocida y utilizará un índice espacial, si está disponible.

Entonces, reescribiendo su consulta como:

WITH test_geom (geom) AS 
   (SELECT ST_MakeEnvelope(-410961,4920492,-402305,4926887,3857))
  SELECT COUNT(*) as value
    FROM alasarr_social_mv mv, test_geom tg 
   WHERE ST_Intersects(mv.the_geom_webmercator, tg.geom)

ahora usará un índice espacial. Del mismo modo, && ahora usará un índice para verificar un cuadro delimitador y (aunque no puedo probar sus datos), debería ser más rápido que ST_Intersects.

Curiosamente, en su consulta, ST_Intersects está utilizando un índice de escaneo de mapa de bits (no un resumen), mientras que && no está utilizando ningún índice. Entonces, ambas consultas serán más rápidas con el CTE, pero && ahora deberían ser más rápidas que ST_Intersects.

Hay más explicaciones de lo que está sucediendo en esta pregunta y sus respuestas / comentarios .

EDITAR : Para hacer esto explícito, si observa la definición de ST_Intersects en postgis.sql (que se llama CREATE EXTENSION postgisy se encuentra en el directorio contrib de su instalación de Postgres), verá:

---- Inlines index magic
CREATE OR REPLACE FUNCTION ST_Intersects(geom1 geometry, geom2 geometry)
    RETURNS boolean
    AS 'SELECT $1 OPERATOR(&&) $2 AND _ST_Intersects($1,$2)'
    LANGUAGE 'sql' IMMUTABLE ;

incluyendo el comentario: índice mágico en línea.

John Powell
fuente
1
No creo que ST_Intersects use && bajo el capó.
Evan Carroll
44
@EvanCarroll, revisa mi edición. Eche un vistazo a postgis.sql, donde se definen las funciones, y debería ser más claro.
John Powell