Perché st_intersects è più veloce di &&


10

È una tabella di punti. ~ 1 milione di record

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

Sembra che le forze di st_intersects utilizzino gli indici spaziali, ma && no.

Campione 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

Campione 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

Versione 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
Non incollare schermate di testo nella domanda. Puoi copiarli e incollarli come codice? Non riesco a leggerli per aiutarti.
Evan Carroll,

Fatto. Penso che sia un po 'meglio ora
alasarr

Ho provato a eseguire il backport dei tuoi nuovi piani di query in quello. Sentiti libero di aggiornare i piani ma cerca di mantenere lo stile.
Evan Carroll,

4
Dai un'occhiata a questa domanda . L'operatore && in realtà esegue una query del riquadro di selezione, mentre ST_Intersects utilizza una query del riquadro di selezione per determinare quali geometrie testare per il confronto effettivo, quindi ci si aspetta che && sia più veloce. Tuttavia, è probabile che l'uso di ST_MakeEnvelope a destra di && stia facendo in modo che il pianificatore di query scelga una scansione della tabella completa per qualche motivo (come è evidente dalla spiegazione). Prova a creare prima la geometria, in un CTE, e vedi se riesci a "ingannare" l'ottimizzatore.
John Powell,

Hai ragione! funziona all'interno di un CTE
alasarr

Risposte:


16

Questo tipo di scoperta emerge abbastanza spesso, ed è un po 'oscuro, quindi vale la pena ribadire. Se si definisce una geometria all'interno di una funzione che la utilizza, come ST_Intersects o && (che ST_Intersects utilizza sotto il cofano), il planner delle query sceglie una scansione completa della tabella, in quanto "esso" non ha conoscenza del risultato della creazione della geometria funzione, ovvero ST_MakeEnvelope in questo caso. Se si definisce la geometria che si desidera verificare l'intersezione in un CTE, l'ottimizzatore ha a che fare con una quantità nota e utilizzerà un indice spaziale, se disponibile.

Quindi, riscrivendo la query come:

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)

ora utilizzerà un indice spaziale. Allo stesso modo, && ora utilizzerà un indice per verificare la presenza di un rettangolo di selezione e, (anche se non riesco a verificare i tuoi dati), dovrebbe essere più veloce di ST_Intersects.

È interessante notare che nella tua query, ST_Intersects sta usando un indice di scansione bitmap (non un dato di sintesi), mentre && non usa alcun indice. Pertanto, entrambe le query saranno più veloci con il CTE, ma ora && dovrebbe essere più veloce di ST_Intersects.

C'è più spiegazione di ciò che sta succedendo in questa domanda e delle sue risposte / commenti .

EDIT : per renderlo esplicito, se guardi la definizione di ST_Intersects in postgis.sql (che viene chiamato CREATE EXTENSION postgise trovato nella directory contrib della tua installazione di Postgres), vedrai:

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

incluso il commento: incorpora la magia dell'indice.


1
Non credo che ST_Intersects usi && sotto il cofano.
Evan Carroll,

4
@EvanCarroll, controlla la mia modifica. Dai un'occhiata a postgis.sql, dove sono definite le funzioni, e dovrebbe essere più chiaro.
John Powell,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.