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

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.


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,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.