Планировщик может недооценить кардинальность и стоимость шагов, выбрать слишком дешевый для себя план, а в реальности получить Nested Loop там, где выгоднее Hash Join, или Seq Scan вместо Index Scan.
Бывает и так, что дерево запроса устроено неудачно: нужные условия спрятаны, и часть эффективных алгоритмов просто не рассматривается.
Эти случаи закрывает расширение
pgpro_planner. Оно переписывает неудобные для оптимизатора куски дерева запроса в более дружелюбный вид еще до того, как основной планировщик выберет план. Ниже — три примера небольших оптимизаций с большим эффектом.
✔️
IN (VALUES ...) → = ANY(array)В одном запросе из мира 1С фильтры были оформлены как
IN (VALUES ...) для numeric и bytea. До обновления статистики он работал быстрее 1 мс, после — около 7,5 с. В плане львиная доля времени ушла в Nested Loop Semi Join и материализацию маленьких таблиц из
VALUES. Индекс почти не помогал, потому что сравнение было внутри join-фильтра, а не в Index Cond. Решение: собрать константы
VALUES в массив и переписать условие как = ANY(array). Тогда индекс попадает в Index Cond, из плана исчезают лишние Semi Join и временные таблицы, а время падает с секунд до долей миллисекунды. Эту функциональность в итоге закоммитили в PostgreSQL 18.
✔️ Простейшая арифметика, которая ломает индекс
Выражения
x + 0, x * 1, x / 1, x - 0 равны x, но планировщик не обязан это распознавать. Поэтому условие x + 0 > 900 может дать Seq Scan, хотя x > 900 позволяет построить Index Only Scan. Шаг упрощения делает предикат индексируемым еще до планирования.✔️ Memoize для коррелированных подзапросов
В бенчмарке один и тот же подзапрос выполнялся 14 835 720 раз и давал Execution Time 496 690 мс. Узел Memoize кеширует результат по ключу параметра и возвращает его из кеша при повторе. Итог — 115 528 мс, то есть ускорение в четыре с лишним раза.
Подключается это все как обычное расширение оптимизатора: через
LOAD pgpro_planner в сессии или через shared_preload_libraries с перезагрузкой. Затем включают
pgpro_planner.enable = true и при необходимости управляют отдельными функциями: преобразованием VALUES, упрощением тривиальных выражений и Memoize.Подробности читайте на Хабре.
