Dlaczego st_intersects jest szybszy niż &&

10

To jest tabela punktów. ~ 1 milion rekordów

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

Wygląda na to, że st_intersects zmusza do użycia indeksów przestrzennych, ale && nie.

Próbka przy użyciu 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

Próbka przy użyciu &&(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

Wersja 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
źródło
2
Nie wklejaj zrzutów ekranu tekstu do pytania. Czy możesz je skopiować i wkleić jako kod? Nie mogę ich przeczytać, żeby ci pomóc.
Evan Carroll
Gotowy. Myślę, że jest teraz trochę lepiej
alasarr
Próbowałem przenieść do tego nowe plany zapytań. Aktualizuj plany, ale staraj się zachować styl.
Evan Carroll
4
Spójrz na to pytanie . Operator && faktycznie wykonuje zapytanie ramki granicznej, podczas gdy ST_Intersects używa zapytania ramki granicznej, aby określić, które geometrie mają być testowane w celu faktycznego porównania - więc można oczekiwać, że && będzie szybsze. Jednak jest prawdopodobne, że użycie ST_MakeEnvelope po prawej stronie && powoduje, że planista zapytań z jakiegoś powodu wybrał skanowanie pełnej tabeli (jak wynika z wyjaśnienia). Najpierw spróbuj utworzyć geometrię w CTE i sprawdź, czy możesz oszukać optymalizator.
John Powell,
Masz rację! działa wewnątrz CTE
alasarr

Odpowiedzi:

16

Tego rodzaju odkrycia pojawiają się dość często i są nieco niejasne, dlatego warto je powtórzyć. Jeśli zdefiniujesz geometrię w funkcji, która jej używa, takiej jak ST_Intersects lub && (której ST_Intersects używa pod maską), wówczas planista zapytań wybiera pełny skan tabeli, ponieważ „nie” wie o wyniku utworzenia geometrii funkcja, tj. ST_MakeEnvelope w tym przypadku. Jeśli zdefiniujesz geometrię, którą chcesz sprawdzić pod kątem przecięcia w CTE, wówczas optymalizator zajmie się znaną wielkością i użyje indeksu przestrzennego, jeśli jest dostępny.

Przepisując zapytanie jako:

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)

będzie teraz korzystać z indeksu przestrzennego. Podobnie, && będzie teraz używać indeksu, aby sprawdzić pole ograniczające, i (chociaż nie mogę przetestować na podstawie danych), powinno być szybsze niż ST_Intersects.

Co ciekawe, w zapytaniu ST_Intersects używa indeksu skanowania bitmapy (nie gist), a && nie używa indeksu. Oba zapytania będą szybsze dzięki CTE, ale && powinny być teraz szybsze niż ST_Intersects.

Istnieje więcej wyjaśnień na temat tego pytania i jego odpowiedzi / komentarzy .

EDYCJA : Aby to wyjaśnić, jeśli spojrzysz na definicję ST_Intersects w postgis.sql (która jest wywoływana CREATE EXTENSION postgisi znajduje się w katalogu contrib instalacji Postgres), zobaczysz:

---- 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 ;

w tym komentarz: inlines index magic.

John Powell
źródło
1
Nie sądzę, że ST_Intersects używa && pod maską.
Evan Carroll
4
@EvanCarroll, sprawdź moją edycję. Spójrz na postgis.sql, w którym funkcje są zdefiniowane, i powinno być jaśniej.
John Powell,