Перейти к содержанию

Как читать план запроса

Как читать план запроса

План запроса — это карта того, как СУБД реально выполняет твой SQL. Не как «в теории», а как конкретно: чем читает, как соединяет, где сортирует и где тратит время.

postgresql-query-plan-overview.svg

Как посмотреть план

Добавь EXPLAIN перед запросом:

SQL
EXPLAIN
SELECT *
FROM sales
WHERE amount > 100;

Самые полезные варианты:

  • EXPLAIN — показывает оценку (cost/rows/width).
  • EXPLAIN ANALYZE — выполняет запрос и показывает фактическое время/строки.
  • EXPLAIN (ANALYZE, BUFFERS, VERBOSE) — плюс буферы и расширенные детали.

Простая аналогия

EXPLAIN — это когда ты читаешь рецепт и думаешь, что все получится. EXPLAIN ANALYZE — когда ты реально готовишь и понимаешь, что духовки нет.

Осторожно на проде

EXPLAIN ANALYZE реально выполняет запрос. На тяжелых выборках может быть больно.

Что ты увидишь внутри

План — это дерево узлов. Каждый узел отвечает за конкретную операцию:

  • Scan — как читаем (Seq / Index / Bitmap).
  • Join — как соединяем (Hash / Merge / Nested Loop).
  • Sort / Aggregate / Window — сортировки, агрегации, окна.
  • Motion — только в MPP/Greenplum (Gather / Redistribute / Broadcast).
  • Spill to disk — сброс на диск, если памяти не хватило на sort/hash.

postgresql-query-plan-nodes.svg

Главное, на что смотреть

  1. actual time — где реально тратится время.
  2. rows (estimated vs actual) — если различие сильное, проблемы со статистикой.
  3. Join-стратегии — Nested Loop на больших объемах часто беда.
  4. Sort / Hash — были ли спиллы на диск (workfiles).
  5. Motion в GP — узкие места и перекладывание данных между сегментами.

Быстрая шпаргалка по метрикам

  • cost — не миллисекунды, а абстрактные единицы оптимизатора.
  • rows — сколько строк ожидается и сколько получилось.
  • width — средний размер строки.
  • actual time — реальные времена выполнения.
  • buffers — попадания в кэш и чтения с диска.

Мини-пример

Запрос:

SQL
EXPLAIN (ANALYZE, BUFFERS)
SELECT region, sum(amount)
FROM sales
WHERE amount > 100
GROUP BY region;

Фрагмент плана (упрощенно):

Text Only
HashAggregate  (cost=... rows=...) (actual time=... rows=...)
  -> Seq Scan on sales  (cost=... rows=...) (actual time=... rows=...)
       Filter: (amount > 100)

Как читать: - Сначала читаем таблицу (Seq Scan) и фильтруем строки. - Потом агрегируем по region (HashAggregate).

Что я рекомендую

  • Серия статей по PG (Tensor): https://habr.com/ru/companies/tensor/articles/790282/
  • Репозиторий с материалами: https://github.com/Kilor/PG-for-beginners/tree/main
  • Интерактивные планы: https://explain.tensor.ru/archive/
  • Можно использовать ИИ для объяснений — но выводы перепроверяй.

Что дальше

В отдельном разборе пройдемся по реальному плану GP/PG: где теряется время, как найти узкие места и чем лечить.