Почему st_intersects быстрее чем &&

10

Это таблица очков. ~ 1 млн. Записей

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

Похоже, что st_intersects заставляет использовать пространственные индексы, но && этого не делает.

Пример использования ST_Intersects(282 мс)

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

Пример использования &&(414 мс)

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

Версия 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
источник
2
Не вставляйте скриншоты текста в вопрос. Вы можете скопировать и вставить их как код? Я не могу прочитать их, чтобы помочь вам.
Эван Кэрролл
Выполнено. Я думаю, что немного лучше сейчас
alasarr
Я пытался перенести ваши новые планы запросов в это. Не стесняйтесь обновлять планы, но старайтесь сохранить стиль.
Эван Кэрролл
4
Посмотрите на этот вопрос . Оператор && на самом деле выполняет запрос ограничивающего прямоугольника, тогда как ST_Intersects использует запрос ограничивающего прямоугольника, чтобы определить, какие геометрии проверять для фактического сравнения, поэтому можно ожидать, что && будет быстрее. Однако вполне вероятно, что использование ST_MakeEnvelope справа от && заставляет планировщика запросов по какой-то причине выбрать полное сканирование таблицы (как видно из объяснения). Попробуйте сначала создать геометрию в CTE и посмотрите, сможете ли вы «обмануть» оптимизатор.
Джон Пауэлл
Вы правы! это работает внутри CTE
alasarr

Ответы:

16

Подобные открытия встречаются довольно часто, и они немного неясны, поэтому стоит повторить. Если вы определяете геометрию в функции, которая ее использует, например, ST_Intersects или && (которую ST_Intersects использует под капотом), тогда планировщик запросов выбирает полное сканирование таблицы, так как «он» не знает о результате создания геометрии функция, т. е. ST_MakeEnvelope в этом случае. Если вы определяете геометрию, которую вы хотите проверить на пересечение в CTE, то оптимизатор работает с известным количеством и будет использовать пространственный индекс, если он доступен.

Итак, переписать ваш запрос как:

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)

теперь будет использовать пространственный индекс. Аналогично, && теперь будет использовать индекс для проверки ограничивающего прямоугольника, и (хотя я не могу проверить ваши данные) должен быть быстрее, чем ST_Intersects.

Интересно, что в вашем запросе ST_Intersects использует индекс растрового сканирования (не гист), в то время как && не использует индекс. Таким образом, оба запроса будут выполняться быстрее с CTE, но теперь && должен быть быстрее, чем ST_Intersects.

Есть больше объяснений того, что происходит в этом вопросе и его ответах / комментариях .

РЕДАКТИРОВАТЬ : Чтобы сделать это явным, если вы посмотрите на определение ST_Intersects в postgis.sql (который вызывается CREATE EXTENSION postgisи находится в каталоге contrib вашей установки Postgres), вы увидите:

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

в том числе комментарий: встроенный индекс магии.

Джон Пауэлл
источник
1
Я не думаю, что ST_Intersects использует && под капотом.
Эван Кэрролл
4
@EvanCarroll, проверь мое редактирование. Взгляните на postgis.sql, где определены функции, и это должно быть понятнее.
Джон Пауэлл