Mengapa st_intersect lebih cepat dari &&

10

Ini daftar poin. ~ Catatan 1 juta

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

Sepertinya st_intersects memaksa untuk menggunakan indeks spasial tetapi && tidak.

Sampel menggunakan ST_Intersects(282 ms)

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

Sampel menggunakan &&(414 ms)

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 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
sumber
2
Jangan tempel tangkapan layar teks ke dalam pertanyaan. Bisakah Anda menyalin dan menempelkannya sebagai kode? Saya tidak bisa membacanya untuk membantu Anda.
Evan Carroll
Selesai Saya pikir sedikit lebih baik sekarang
alasarr
Saya mencoba untuk mendukung rencana permintaan baru Anda ke dalamnya. Jangan ragu untuk memutakhirkan paket tetapi cobalah untuk mempertahankan gayanya.
Evan Carroll
4
Lihat pertanyaan ini . Operator && sebenarnya melakukan kueri kotak pembatas, sedangkan, ST_Intersects menggunakan kueri kotak pembatas untuk menentukan geometri mana yang akan diuji untuk perbandingan aktual - sehingga Anda mengharapkan && menjadi lebih cepat. Namun, kemungkinan penggunaan ST_MakeEnvelope di sebelah kanan && membuat perencana kueri memilih pemindaian tabel penuh untuk beberapa alasan (seperti yang terlihat dari penjelasan). Coba buat geometri terlebih dahulu, dalam CTE, dan lihat apakah Anda dapat "menipu" pengoptimal.
John Powell
Kamu benar! itu bekerja di dalam CTE
alasarr

Jawaban:

16

Temuan semacam ini cukup sering muncul, dan agak tidak jelas, jadi layak untuk disajikan kembali. Jika Anda mendefinisikan geometri di dalam fungsi yang menggunakannya, seperti ST_Intersects atau && (yang digunakan ST_Intersects di bawah tenda), maka perencana kueri memilih pemindaian tabel penuh, karena "ia" tidak memiliki pengetahuan tentang hasil dari pembuatan geometri fungsi, yaitu, ST_MakeEnvelope dalam kasus ini .. Jika Anda mendefinisikan geometri yang ingin Anda periksa persimpangan dalam CTE, maka pengoptimal berurusan dengan kuantitas yang diketahui, dan akan menggunakan indeks spasial, jika tersedia.

Jadi, tulis ulang kueri Anda sebagai:

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)

sekarang akan menggunakan indeks spasial. Demikian juga, && sekarang akan menggunakan indeks untuk memeriksa kotak pembatas, dan, (sementara saya tidak dapat menguji data Anda), harus lebih cepat daripada ST_Intersects.

Menariknya, dalam kueri Anda, ST_Intersects menggunakan indeks pemindaian bitmap (bukan inti), sedangkan && tidak menggunakan indeks. Jadi, kedua kueri akan lebih cepat dengan CTE, tetapi && sekarang seharusnya lebih cepat dari ST_Intersects.

Ada lebih banyak penjelasan tentang apa yang terjadi dalam pertanyaan ini dan jawaban / komentarnya .

EDIT : Untuk membuat ini eksplisit, jika Anda melihat definisi ST_Intersects di postgis.sql (yang dipanggil oleh CREATE EXTENSION postgisdan ditemukan di direktori contrib dari instalasi Postgres Anda), Anda akan melihat:

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

termasuk komentar: inline magic indeks.

John Powell
sumber
1
Saya tidak berpikir ST_Intersects menggunakan && di bawah tenda.
Evan Carroll
4
@EvanCarroll, periksa edit saya. Lihat postgis.sql, di mana fungsinya didefinisikan, dan itu harus lebih jelas.
John Powell