Я пытаюсь улучшить производительность для запроса ниже. Независимо от того, как я пишу запрос (подзапрос в предложении FROM, подзапрос в предложении WHERE) postgres настаивает на запуске всех ~ 570K строк через дорогую функцию ST_DWITHIN, даже если есть только 60 строк, в которых county = 24. Как я могу заставить postgres фильтровать на county = 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
источник
point
копирую ~ 60 строк, в которых county = 24, в новую таблицу, запрос занимает всего 0,403 мс по сравнению с 724, поэтому, безусловно, есть большая разница.count(*)
как вопрос стиля. Еслиid
это pkid, как вы говорите, этоNOT NULL
означает, что они одинаковы. За исключениемcount(id)
того недостатка, что вы должны задать этот вопрос, еслиid
обнуляется.Ответы:
Вы можете увидеть проблему с ожидаемым и фактическим количеством строк. Планировщик считает, что есть 7 437 строк, но только 63. Статистика отключена. Интересно также, что он не использует поиск по индексу (индексу) с помощью ограничивающего прямоугольника, с помощью которого
DWithin
можно вставить результат\d point
. Какая версия PostGIS и PostgreSQL?Попробуйте запустить
ANALYZE point
. Вы получаете тот же план, когда вы перемещаете условие вверх?источник
Как примечание стороны, есть разумный шанс , что такое поведение изменяется в PostGIS 2.3.0 , если вы хотите назвать это ошибкой.
Из документации по PostgreSQL
Таким образом, стоимость по умолчанию была 1 (очень дешево).
D_Within
Использование индекса GIST очень дешево. Но это было увеличено до 100 (по доверенности внутреннего_ST_DWithin
).Я сам не большой поклонник метода CTE. CTE - это забор оптимизации. Выполнение этого таким образом устраняет потенциальную возможность для дальнейшей оптимизации. Если Saner по умолчанию исправить это, я бы предпочел обновить. В конце концов, мы должны выполнить работу, и этот метод явно работает для вас.
источник
Благодаря подсказке Джона Пауэлла я пересмотрел запрос, чтобы поместить условие ограничения округа в запрос с / CTE, и это значительно улучшило производительность до 222 мс против 700. До далека от 0,74 мс я получаю, когда данные находятся в его собственный стол. Я до сих пор не уверен, почему планировщик не ограничивает набор данных перед запуском дорогой функции postgis, и мне придется попробовать с большими наборами данных, когда они у меня есть, но сейчас это решение этой уникальной ситуации.
источник
Вы должны создать индекс для
zipcode(county, zipcode)
, который должен дать вам индекс только для сканирования по z.Вы также можете поэкспериментировать с
btree_gist
расширением создающего либоpoint(zipcode, geom)
индекс илиpoint(geom, zipcode)
иzipcode(zipcode, county)
индекс.источник