kb/planner
Жизнь запроса от текста до исполнителя, стоимостная модель и кардинальность, статистика pg_stats и гистограммы, методы доступа и алгоритмы соединений, чтение EXPLAIN ANALYZE.
У каждого узла два числа: startup cost (до первой строки) и total cost (до всех). У Seq Scan startup ~0, у Sort - большой. LIMIT смещает выбор в пользу плана с маленьким startup.
Nested loop хорош при малом внешнем входе и индексе внутри; hash join - для больших входов по равенству (нужен work_mem); merge join - когда входы уже отсортированы. Memoize кеширует поиски nested loop.
Запрос проходит пять стадий: разбор, семантический анализ, переписывание, планирование, выполнение. Стадии 1-3 определяют «что» получить, стадия 4 - «как». Один текст даёт одно дерево запроса, но разные планы.
Четыре способа достать строки: Seq Scan (всё подряд), Index Scan (мало строк, random I/O), Bitmap Heap Scan (средняя доля, страницы пачкой), Index-Only Scan (данные из индекса, если visibility map подтверждает).
Третья стадия - система правил. Она раскрывает представления (view - это сохранённый запрос, не данные) и добавляет условия RLS. Планировщик потом сплющивает подзапрос, поэтому view сам по себе не замедляет запрос.
Число порядков соединения растёт быстрее факториала; до geqo_threshold (12) планировщик ищет порядок динамическим программированием, дальше - генетическим GEQO. Кеш плана: после ~5 вызовов custom может стать generic.
По умолчанию планировщик считает колонки независимыми и перемножает селективности - для коррелированных это занижает оценку. CREATE STATISTICS (dependencies, ndistinct, mcv) собирает статистику по группе колонок вместе.
Селективность - доля строк, проходящих условие. Index Scan дёшев при малой доле и круто дорожает; Seq Scan стоит одинаково. Линии пересекаются на единицах процентов - это точка перелома, зависит от random_page_cost.
Планировщик не читает данные при планировании - берёт сводку из pg_statistic (view pg_stats), собранную ANALYZE: null_frac, n_distinct, MCV с частотами и гистограмму равной площади. Устаревшая сводка - частый корень плохих планов.
Стоимость - условные единицы, где последовательное чтение страницы = 1.0. Seq Scan стоит relpages × seq_page_cost + reltuples × cpu_tuple_cost. Планировщик выбирает план с минимальной оценкой, а не «правильный».