Обмежте рядки за допомогою просторової функції


9

Я намагаюся підвищити ефективність для запиту нижче. Незалежно від того, як я запитую запит (підзапит у пункті ВІД, підзапит у пункті БУДЕ), postgres наполягає на виконанні всіх ~ 570К рядків через дорогу функцію ST_DWITHIN, навіть якщо в окрузі є лише 60 рядків. Як я можу отримати postgres для фільтра по округу = 24 ДО ПЕРЕД пробігу через функцію postgis, яка, мені здається, була б набагато швидшою та набагато ефективнішою? 700 мс не викликає занадто великого занепокоєння, але оскільки ця таблиця зростає до 10М + я переживаю за продуктивність.

Також слід зазначити, що p.id - це первинний ключ, p.zipcode - індекс fk, z.county - індекс fk, а p.geom - індекс GiST.

Запит:

EXPLAIN ANALYZE
  SELECT count(p.id)
  FROM point AS p
  LEFT JOIN zipcode AS z
    ON p.zipcode = z.zipcode
  WHERE z.county = 24
    AND ST_DWithin(
      p.geom, 
      ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269), 
      16090.0,
      false
    )

ПОЯСНІТЬ АНАЛІЗ:

Aggregate  (cost=250851.91..250851.92 rows=1 width=4) (actual time=724.007..724.007 rows=1 loops=1)
  ->  Hash Join  (cost=152.05..250851.34 rows=228 width=4) (actual time=0.359..723.996 rows=51 loops=1)
        Hash Cond: ((p.zipcode)::text = (z.zipcode)::text)
        ->  Seq Scan on point p  (cost=0.00..250669.12 rows=7437 width=10) (actual time=0.258..723.867 rows=63 loops=1)
              Filter: (((geom)::geography && '0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography) AND ('0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography && _st_expand((geom)::geography, 16090::double precision)) AND _st_dwithin((g (...)
              Rows Removed by Filter: 557731
        ->  Hash  (cost=151.38..151.38 rows=54 width=6) (actual time=0.095..0.095 rows=54 loops=1)
              Buckets: 1024  Batches: 1  Memory Usage: 3kB
              ->  Bitmap Heap Scan on zipcode z  (cost=4.70..151.38 rows=54 width=6) (actual time=0.023..0.079 rows=54 loops=1)
                    Recheck Cond: (county = 24)
                    Heap Blocks: exact=39
                    ->  Bitmap Index Scan on fki_zipcode_county_foreign_key  (cost=0.00..4.68 rows=54 width=0) (actual time=0.016..0.016 rows=54 loops=1)
                          Index Cond: (county = 24)
Planning time: 0.504 ms
Execution time: 724.064 ms

Можливо, спробуйте змінити рядок "точка як p зліва приєднати поштовий індекс як z" на щось на кшталт "пункт як p зліва приєднатися (ВИБІР * ВІД zipcode WHERE zipcode.county = 24) на z"?
weiji14

Просто спробував, ті ж результати. Коли я копіюю ~ 60 pointрядків, де округ = 24, в нову таблицю все самостійно, запит займає лише .453ms порівняно з 724, тому, безумовно, є велика різниця.
Джош

1
Ви повинні використовувати count(*)як питання стилю. Якщо idце pkid , як ви говорите, це NOT NULLщо означає , що вони однакові. За винятком count(id)недоліку, який потрібно задати цим питанням, якщо він idє нульовим.
Еван Керролл

1
Чи можу я запитати, чому ви використовуєте ліве зовнішнє з'єднання? Спробуйте змінити його на внутрішнє з'єднання ... Результати повинні бути однаковими
MickyT

Якщо z.country є обмежуючим фактором, я б запропонував вам покласти це спочатку в CTE-запит, а потім просто перевірити ці результати на перехрестя з вашою цікавою точкою. Оскільки в цьому випадку просторовий індекс, мабуть, менш вибірковий, ніж округ = 24, він лише заважає.
Джон Пауелл

Відповіді:


3

Ви можете бачити проблему з очікуваними та фактичними підрахунками рядків. Планувальник вважає, що налічується 7,437 рядків, проте лише 63. Статистика вимкнена. Цікаво, що ви не можете використовувати пошук обмежувального індексу (індексу), за допомогою якого DWithinможна вставити результат \d point. Яка версія PostGIS та PostgreSQL?

Спробуйте запустити ANALYZE point. Чи отримуєте ви той самий план, коли ви пересуваєте стан вгору?

JOIN zipcode AS z
  ON p.zipcode = z.zipcode
  AND z.county = 24

Я зробив аналіз пробігу, а також спробував нову стан AND у ВКЛ, але все одно отримував 700 мс пробігу. Це PGSQL 9.4 та PostGIS 2.2.
Джош

2

В якості додаткового зауваження, є обґрунтований шанс, що ця поведінка буде змінена в PostGIS 2.3.0, якщо ви хочете назвати її помилкою.

З документів на PostgreSQL

Позитивне число, що дає оціночну вартість виконання функції, в одиницях cpu_operator_cost. Якщо функція повертає набір, це вартість за повернутий рядок. Якщо вартість не визначена, 1 одиниця передбачається для мови C та внутрішніх функцій, а 100 одиниць для функцій на всіх інших мовах. Більші значення змушують планувальника намагатися уникати оцінки функції частіше, ніж це потрібно.

Тож вартість за замовчуванням становила 1 (дуже дешево). D_Withinвикористання індексу GIST дуже дешево. Але це було збільшено до 100 (за допомогою внутрішнього проксі _ST_DWithin).

Я сам не є великим прихильником методу CTE. CTE - це огорожа для оптимізації. Таким чином, це робить такий спосіб позбавляє потенційної можливості для майбутньої оптимізації. Якщо виправлені за замовчуванням безпечніші, я б скоріше оновив. Зрештою, ми повинні виконати роботу, і цей метод явно працює для вас.


1

Завдяки натяку Джона Пауелла я переглянув запит, щоб поставити обмежувальну умову графства на запит з / CTE, і це покращило продуктивність трохи до 222 мс проти 700. Досі далекий крик від .74 мс, який я отримую, коли дані є власна таблиця. Я до сих пір не впевнений, чому планувальник не обмежує набір даних перед запуском дорогої функції postgis, і мені доведеться спробувати з більшими наборами даних, коли у них є, але це, здається, є рішенням цієї унікальної ситуації на даний момент.

with points as (
   select p.id, p.geom from point p inner join zipcode z
   on p.zipcode = z.zipcode
   where county = 24
   ) 


SELECT count(points.id)
FROM points
WHERE ST_DWITHIN(points.geom, (ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269)), 16090.0, false)

1
Треба було б побачити всі три плани запитів та схему таблиці (запитується у моїй відповіді \ d пункт).
Еван Керролл

0

Ви повинні створити індекс на zipcode(county, zipcode), який повинен давати вам сканування лише з індексом на z.

Ви також можете поекспериментувати з btree_gistрозширенням створює або point(zipcode, geom)індекс або point(geom, zipcode)й zipcode(zipcode, county)індекс.

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