lesson ── postgres-labs ── ~22 мин ── 4 шагов
Посмотрим на стоимость самого планирования и на кеш плана: как
подготовленное выражение после нескольких вызовов переходит с custom
на generic plan и как это вернуть назад. Запусти psql во вкладке
client.
интерактивный sandbox
Поднимется контейнер postgreslab/postgres-base с PostgreSQL 17 и psql. В браузере откроется терминал, база lab уже настроена. Каждый шаг проверяется автоматически. Сеть air-gapped, наружу контейнер не ходит.
stack ── PostgreSQL 17 · psql · 1 GB RAM · air-gapped · самоуничтожается через 45 мин простоя
EXPLAIN ANALYZE
SELECT t.passenger
FROM tickets t
JOIN flights f ON f.flight_id = t.flight_id
JOIN bookings b ON b.book_ref = t.book_ref;
Внизу - строки Planning Time и Execution Time. Их соотношение
говорит, стоит ли кешировать план.
Если планирование сопоставимо с выполнением - план стоит кешировать.
✓ Видно Planning Time против Execution Time.
SHOW join_collapse_limit; -- 8: сколько JOIN волен переставлять
SHOW geqo_threshold; -- 12: с какого числа таблиц включить GEQO
До join_collapse_limit планировщик ищет лучший порядок; сверх - берёт как написано. От geqo_threshold включается генетический поиск.
join_collapse_limit = 1 фиксирует порядок соединений вручную.
✓ Пороги на месте: 8 для перестановки JOIN, 12 для GEQO.
Подготовь выражение и выполни его много раз:
PREPARE q(int) AS SELECT count(*) FROM tickets WHERE flight_id = $1;
EXECUTE q(1); -- повтори 8 раз
SELECT name, generic_plans, custom_plans FROM pg_prepared_statements;
Первые вызовы - custom plan под значение, после нескольких планировщик переходит на generic plan. Предскажи: будет ли generic_plans больше нуля?
Переключение происходит примерно после 5 вызовов.
✓ generic_plans > 0 - выражение перешло на generic plan.
На перекошенных данных generic plan вреден. Заставь планировать каждый раз:
SET plan_cache_mode = force_custom_plan;
DEALLOCATE q;
PREPARE q(int) AS SELECT count(*) FROM tickets WHERE flight_id = $1;
EXECUTE q(1); -- снова 8 раз
SELECT generic_plans, custom_plans FROM pg_prepared_statements;
Теперь generic_plans остаётся нулём - растёт только custom_plans.
force_custom_plan платит временем планирования ради точного плана под значение.
✓ generic_plans = 0 - force_custom_plan планирует под значение каждый раз.
Порядок соединений ищется динамическим программированием до join_collapse_limit (8), дальше берётся как написано; от geqo_threshold (12) включается GEQO. Кеш плана: подготовленное выражение после ~5 вызовов может перейти на generic plan; на перекошенных данных его возвращают на custom через plan_cache_mode = force_custom_plan.
команды
SHOW join_collapse_limit;порог перестановки JOINSELECT generic_plans, custom_plans FROM pg_prepared_statements;счётчики кеша планаSET plan_cache_mode = force_custom_plan;всегда планировать под значениеконцепции