Алена Рыбакина @Alena0704
Core developer
Information
- Rating
- Does not participate
- Location
- Абрамцево, Москва и Московская обл., Россия
- Date of birth
- Registered
- Activity
Specialization
Backend Developer
Middle
Git
Linux
C
PostgreSQL
Bash
Python
Core developer
Сама так подумала)
Спасибо за фидбек и интерес к этой теме)
Добрый день, пока для 5 таблиц хороший пример, для демонстрации того, о чем говорилось выше я не успела сформировать, но нашла отличный пример из регрессионных тестов Postgres (ветка master).
Кстати, чтобы получить случай тот, что вы хотите - чтобы планнер строил план, как написано, достаточно поставить join_collapse_limit = 1, тогда Postgres построит план так, как вы указали в запросе без использования процедуры планирования, но я не советую это делать. Случаи, когда это может дать ускорение на мой взгляд редкие.
И так, для своего примера использовала таблицу и данные из Postgres, которые он использует для регрессионных тестов.
Обратите внимание, что если вы построете план, как есть (второй план), у вас для Nested Loop вместо 7500 туплов приходит на обработку 75000000, потому что а первом плане, вы фильтруете условием (a.hundred = c.hundred) и по сути у вас получаются все уникальные туплы из столбца а и на Hash Join вы обрабатываете меньшее количество туплов, чем во втором плане.
Другой пример, связанный с параметризацией, содержит в таблицах малое количество данных, потому не будет так очевиден по времени, но по количеству обрабатываемых туплов, можно сделать выводы:
Обратите внимание на условие Hash Cond: (nt2.id = nt3.nt2_id) во втором запросе (где план формируется практически, как мы написали), оно в запросе 2, когда мы дали возможность планнеру подумать над планом перенесло его в фильтр сканирования индекса в первом запросе:
Это позволло перед выполнения Join ноды (Nested Loop) сократить немного объем данных. в данном примере на один тупл, конечно, но представьте себе профит, если увеличить размер таблицы, насколько сократиться время выполнения, потому что будет меньше объема данных обрабатываться.
Остальные, похожие примеры вы можете сами посмотреть, если добавить в конфигрурацию постреса join_collapse_limit=1, рестарт инстанса и запустите make installcheck-world. Конечно, у вас в регрессионных тестах будут отображены изменения планов, но думаю вы найдете дополнительные примеры поясняющие ответ на ваш вопрос. Если не очень понятно, могу скинуть код bash.
Он вышел из статуса “эксперементальныйй”, но не достиг вершины статуса своего развития) Можете найти его в Enterprise16
https://postgrespro.ru/docs/enterprise/16/realtime-query-replanning-how
То, что предложили вы, может сработать для небольших по сложности запросов (если в них указано малое количество таблиц). Потому что связать их как есть бывает дорого. Для оптимизатора стоит цель сформировать такой план с таким порядком соединений, чтобы на уровне выше executor обрабатывал как можно меньшее количество строк, что можно для нод находящимся на уровне ниже. Кстати, это могут жать параметризованные ноды, так как из-за наличия условия соединения, что фильтруют туплы, у нас получается меньшее количество туплов на выходе для выполнения ноды выше. Чтобы найти правильный порядок соединений, оптимизатору нужно пересмотреть комбинацию таблиц для составления справедливых пар (или джойнов). Под словом справедливый я подразумеваю, что иногда невозможно составить джойн двух таблиц из-за сохранения логической эквивалентности.
К тому же, стоит учесть, что оптимизатор умеет переставлять местами inner join, но не может это сделать с outer join.
Вы можете посмотреть доклад из PGConf Canada 2024, чтобы понять подробнее, как оптимизатор работает, если интересно (с 12 слайда).
https://speakerdeck.com/yuyawatari/performance-improvements-of-partitioning-past-and-future-pgconf-dot-dev-2024?slide=12
В текущей статье было предоставлено описание работы базового прототипа, но можно данные триггеры установить на ноды, запросы и пр.
c 16 версии Enterprise. В будущем скорее всего предоставим патч в коммьюнити