Por que o st_intersects é mais rápido que &&

10

É uma tabela de pontos. ~ 1 milhão de registros

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

Parece que st_intersects força o uso de índices espaciais, mas o && não.

Amostra 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

Amostra 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

Versão 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
fonte
2
Não cole capturas de tela de texto na pergunta. Você pode copiar e colar aqueles como código? Não consigo lê-los para ajudá-lo.
Evan Carroll
Feito. Eu acho que é um pouco melhor agora
alasarr 31/08/17
Tentei fazer o backport dos seus novos planos de consulta para isso. Sinta-se livre para atualizar os planos, mas tente manter o estilo.
Evan Carroll
4
Dê uma olhada nesta pergunta . O operador && realmente faz uma consulta de caixa delimitadora, enquanto ST_Intersects usa uma consulta de caixa delimitadora para determinar quais geometrias devem ser testadas para comparação real - portanto, você esperaria que o&& fosse mais rápido. No entanto, é provável que o uso de ST_MakeEnvelope à direita de && esteja fazendo com que o planejador de consultas escolha uma varredura completa da tabela por algum motivo (como é evidente na explicação). Tente criar a geometria primeiro, em um CTE e veja se você pode "enganar" o otimizador.
John John Powell
Você está certo! ele funciona dentro de uma CTE
alasarr

Respostas:

16

Esse tipo de descoberta surge com bastante frequência e é um pouco obscuro, portanto vale a pena reafirmar. Se você definir uma geometria em uma função que a utilize, como ST_Intersects ou && (que ST_Intersects usa sob o capô), o planejador de consultas escolherá uma varredura de tabela completa, pois "it" não tem conhecimento do resultado da criação da geometria função, ou seja, ST_MakeEnvelope neste caso. Se você definir a geometria que deseja verificar a interseção em um CTE, o otimizador estará lidando com uma quantidade conhecida e usará um índice espacial, se disponível.

Então, reescrevendo sua 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)

agora usará um índice espacial. Da mesma forma, && agora usará um índice para verificar se há uma caixa delimitadora e, (embora eu não possa testar seus dados), deve ser mais rápido que ST_Intersects.

Curiosamente, na sua consulta, ST_Intersects está usando um índice de verificação de bitmap (não uma essência), enquanto && não está usando nenhum índice. Portanto, ambas as consultas serão mais rápidas com o CTE, mas && agora deve ser mais rápido que ST_Intersects.

Há mais explicações sobre o que está acontecendo nesta pergunta e suas respostas / comentários .

EDIT : Para tornar isso explícito, se você observar a definição de ST_Intersects no postgis.sql (que é chamado CREATE EXTENSION postgise encontrado no diretório contrib da sua instalação do Postgres), você 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 ;

incluindo o comentário: inlines index magic.

John Powell
fonte
11
Eu não acho que ST_Intersects use && sob o capô.
Evan Carroll
4
@EvanCarroll, verifique minha edição. Dê uma olhada no postgis.sql, onde as funções são definidas, e isso deve ser mais claro.
John Powell