ST_Distance не використовує індекс для просторового запиту


10

Я не можу запустити PostGIS 2.1 на PostgreSQL 9.3.5, щоб використовувати просторовий індекс навіть для найпростіших запитів. Весь набір даних складає 8 мільйонів точок (населення граф сітки тут) . Таблиця створена як

CREATE TABLE points (
    population DOUBLE PRECISION NOT NULL,
    location GEOGRAPHY(4326, POINT) NOT NULL
)
CREATE INDEX points_gix ON points USING GIST(location);

Запити настільки ж прості, як і отримуються

SELECT SUM(population)
FROM points
WHERE ST_Distance(
    location,
    ST_GeographyFromText('SRID=4326; POINT(0 0)')
) < 1000

PostgreSQL завжди використовує Seq-сканування для цього, я спробував підмножину на 10000 балів - все ще Seq-сканування. Будь-які ідеї?


3
Ви не використовуєте жодної функції, яка може використовувати індекс. Використовуйте замість st_dwithin. Тоді fuction спочатку зробить сканування індексу.
Nicklas Avén

Подумайте, що робить ваш запит - обчисліть відстань від кожної точки таблиці до фіксованої точки - і ви зрозумієте, чому не можна використовувати індекс . Замість цього скористайтеся оператором, який може використовувати індекс, наприклад, ST_DWithin
Vince

Відповіді:


19

ST_Distance насправді обчислює відстань між усіма парами точок, тому як такий жоден індекс не може бути використаний. Тож ваш запит зробить сканування послідовності, а потім вибере ті геометрії, які менші відстані, яку ви вказали. Ви шукаєте ST_DWithin , який використовує індекс.

SELECT SUM(population) FROM points 
WHERE ST_DWithin(location, ST_GeographyFromText('SRID=4326; POINT(0 0)'), 1000);

ST_Distance є більш корисним для замовлення результатів, часто спільно з ORDER BY та / або LIMIT, отриманих із запитами, які використовують індекс.


1
Дякую. Я справді повинен прочитати документи, перш ніж задавати питання.
синапс

1
ОЦЕ ТАК! ДЯКУЮ ТОБІ! Ви просто "прискорили" мій повільний запит, наприклад, в 100 разів і більше через зміну st_distance на st_dwithin. (Я кажу "прискорено", тому що цього ніколи не повинно було статися, якби я був більш обережним)
Хенді Іраван

1
@HendyIrawan. Будь ласка. Це легко зробити помилку.
Джон Пауелл

@ JohnPowellakaBarça Я додав ще одну оптимізацію (хоча дуже втрачав , але я додав відповідь на свій випадок), але ви вказали мені в правильному напрямку, дякую.
Хенді Іраван

4

Як сказав @ JohnPowellakaBarça ST_DWithin()- це шлях, коли ви хочете коректності .

Однак у моєму випадку я хочу лише орієнтовної оцінки, тому навіть ST_DWithin()була занадто дорогою (у вартості запитів) для моїх потреб. Я використовував &&і ST_Expand(box2d)(не помиляйтеся з geometryверсією) замість цього. Приклад:

SELECT * FROM profile
  WHERE
    address_point IS NOT NULL AND
    address_point && CAST(ST_Expand(CAST(ST_GeomFromText(:point) AS box2d), 0.5) AS geometry;

Що відразу стане очевидним, що ми маємо справу з градусами замість метрів, а використовуємо обмежувальне поле замість кола в сфероїді. У моєму випадку використання це скорочується з 24 мс до всього 2 мс (локально в SSD). Однак для моєї виробничої бази в AWS RDS PostgreSQL з одночасними з'єднаннями та навряд чи великими квотами IOPS (100 IOPS) оригінальний ST_DWithin()запит витрачає занадто багато IOPS і може виконати понад 2000 мс і набагато гірше, коли квота IOPS вичерпана.

Це не для всіх, але якщо ви можете пожертвувати певною точністю для швидкості (або для економії IOPS), то такий підхід може бути для вас. Як ви бачите в планах запитів нижче, ST_DWithinвсе ще потрібен просторовий фільтр всередині Bitmap Heap Scan на додаток до повторної перевірки Cond, тоді як &&для геометрії коробки не потрібен фільтр і використовується лише повторна перевірка Cond.

Я також зауважив, що IS NOT NULLмає значення, без цього у вас залишиться гірший план запитів. Здається, індекс GIST недостатньо розумний для цього. (звичайно, це не потрібно, якщо ваш стовпець є NOT NULL, в моєму випадку він NULLздатний)

Таблиця 20000 рядків, ST_DWithin(geography, geography, 100000, FALSE)на AWS RDS 512 Мб оперативної пам’яті з 300 IOPS:

Aggregate  (cost=4.61..4.62 rows=1 width=8) (actual time=2011.358..2011.358 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..4.61 rows=1 width=0) (actual time=1735.025..2010.635 rows=1974 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)))
        Filter: (((status)::text = 'ACTIVE'::text) AND ((gender)::text = 'MALE'::text) AND (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(address_point, '100000'::double precision)) AND _st_dwithin(address_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, false)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(hometown_point, '100000'::double precision)) AND _st_dwithin(hometown_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, false))))
        Rows Removed by Filter: 3323
        Heap Blocks: exact=7014
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=1716.425..1716.425 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.42 rows=1 width=0) (actual time=1167.698..1167.698 rows=16086 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.42 rows=1 width=0) (actual time=548.723..548.723 rows=7846 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
Planning time: 47.366 ms
Execution time: 2011.429 ms

Таблиця 20000 рядків &&і ST_Expand(box2d)на AWS RDS 512 Мб оперативної пам’яті з 300 IOPS:

Aggregate  (cost=3.85..3.86 rows=1 width=8) (actual time=584.346..584.346 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..3.85 rows=1 width=0) (actual time=555.048..584.083 rows=1154 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)))
        Filter: (((status)::text = 'ACTIVE'::text) AND ((gender)::text = 'MALE'::text))
        Rows Removed by Filter: 555
        Heap Blocks: exact=3812
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=553.091..553.091 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.42 rows=1 width=0) (actual time=413.074..413.074 rows=4850 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.42 rows=1 width=0) (actual time=140.014..140.014 rows=3100 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
Planning time: 0.673 ms
Execution time: 584.386 ms

Знову з більш простим запитом:

Таблиця 20000 рядків, ST_DWithin(geography, geography, 100000, FALSE)на AWS RDS 512 Мб оперативної пам’яті з 300 IOPS:

Aggregate  (cost=4.60..4.61 rows=1 width=8) (actual time=36.448..36.448 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..4.60 rows=1 width=0) (actual time=7.694..35.545 rows=2982 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)))
        Filter: (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(address_point, '100000'::double precision)) AND _st_dwithin(address_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, true)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(hometown_point, '100000'::double precision)) AND _st_dwithin(hometown_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, true)))
        Rows Removed by Filter: 2322
        Heap Blocks: exact=2947
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=7.197..7.197 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.41 rows=1 width=0) (actual time=5.265..5.265 rows=5680 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.41 rows=1 width=0) (actual time=1.930..1.930 rows=2743 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
Planning time: 0.479 ms
Execution time: 36.512 ms

Таблиця 20000 рядків &&і ST_Expand(box2d)на AWS RDS 512 Мб оперативної пам’яті з 300 IOPS:

Aggregate  (cost=3.84..3.85 rows=1 width=8) (actual time=6.263..6.264 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..3.84 rows=1 width=0) (actual time=4.295..5.864 rows=1711 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)))
        Heap Blocks: exact=1419
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=4.122..4.122 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.41 rows=1 width=0) (actual time=3.018..3.018 rows=1693 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.41 rows=1 width=0) (actual time=1.102..1.102 rows=980 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
Planning time: 0.399 ms
Execution time: 6.306 ms

1
Добре написати і цікаво.
Джон Пауелл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.