Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE

Рейтинг: 74.1% · 11 голосов
Подробный курс по PostgreSQL: SQL по демобазе, типы данных и JSONB, индексы, транзакции и MVCC, VACUUM, EXPLAIN и планировщик, роли и привилегии, репликация, бэкап, настройка под нагрузку и под 1С. Актуально на 2026.
Ответить
Аватара пользователя
Egor_DBA
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE

Сообщение Egor_DBA »

Оглавление курса (47)
  1. Что такое PostgreSQL: история, философия, где применяется
  2. Урок 1. Что нового в PostgreSQL 15 и далее (15 -> 16 -> 17)
  3. Установка и первый запуск: Linux, Windows, Docker
  4. psql детально: метакоманды и работа в консоли
  5. Демобаза Авиаперевозки Postgres Pro: структура и развёртывание
  6. SELECT: проекция, фильтрация, сортировка
  7. Транзакции и ACID
  8. Уровни изоляции транзакций: Read Committed, Repeatable Read, Serializable
  9. Соединения таблиц (JOIN)
  10. Агрегация: GROUP BY, HAVING, GROUPING SETS
  11. Подзапросы и CTE (WITH), рекурсия
  12. Оконные функции
  13. Изменение данных: INSERT/UPDATE/DELETE, UPSERT, MERGE
  14. Числовые и булевы типы, NULL и трёхзначная логика
  15. Строки, текст, дата и время, таймзоны
  16. Массивы, диапазоны, enum, составные типы
  17. JSON и JSONB: операторы и индексация
  18. DDL: CREATE TABLE, схемы, ALTER
  19. Ограничения целостности
  20. Представления и материализованные представления
  21. Последовательности, IDENTITY, генерируемые столбцы
  22. Индексы: B-tree и когда индекс не используется
  23. Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN
  24. Продвинутые индексы: частичные, по выражению, покрывающие
  25. Обслуживание индексов и таблиц: bloat, REINDEX, CONCURRENTLY
  26. Полнотекстовый поиск
  27. Роли и пользователи
  28. Привилегии: GRANT/REVOKE
  29. Row-Level Security и безопасность данных
  30. Подключение приложения: pg_hba.conf, SSL, пулы соединений
  31. Серверное программирование: функции и процедуры
  32. Триггеры и события
  33. Архитектура PostgreSQL: процессы и память
  34. MVCC: версии строк и видимость
  35. VACUUM, autovacuum, freeze и wraparound
  36. WAL, контрольные точки и долговечность
  37. Блокировки и взаимоблокировки
  38. Планировщик запросов
  39. EXPLAIN и EXPLAIN ANALYZE: чтение планов
  40. Конфигурация сервера: ключевые параметры
  41. Настройка под нагрузку и под 1С
  42. Параллельные запросы и партиционирование
  43. Резервное копирование и восстановление
  44. Репликация: потоковая и hot standby
  45. Логическая репликация и кластерные решения
  46. Внешние данные, расширения и сертификация
  47. Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE (вы здесь)
Урок 46. Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE

В прошлом уроке мы научились снимать план через EXPLAIN. Но голый текстовый план на сотню строк пугает, и глаз цепляется не за то. Этот урок про два навыка сразу: как загнать план в визуализатор explain.tensor.ru и за минуту увидеть проблемное место, и как читать сам EXPLAIN ANALYZE руками - чтобы понимать, что именно подсветил сервис и почему. Разберём cost против реального времени, узлы плана, буферы и пройдём живой сценарий ускорения запроса на демобазе Авиаперевозки.

Изображение

Как это работает

explain.tensor.ru - это бесплатный русскоязычный веб-сервис анализа планов postgresql от компании Tensor (та самая, что делает СБИС). Идея простая: вы вставляете текст плана, а сервис строит наглядное дерево узлов, считает долю времени по каждому узлу, подсвечивает самый тяжёлый и дописывает рекомендации. То, на что вручную уходит десять минут разглядывания, тут видно сразу.

Важно понимать: сервис ничего не выполняет и к вашей базе не подключается. Он разбирает уже готовый текст плана. Значит, качество разбора целиком зависит от того, насколько полный план вы сняли. Снимете без буферов - не будет анализа ввода-вывода. Снимете без ANALYZE - не будет реального времени, только оценки планировщика.

Ключевая развилка в чтении любого плана: cost против actual time. Cost - это абстрактная оценка планировщика в условных единицах (примерно сколько страниц и строк он ждёт обработать), она нужна оптимизатору, чтобы выбрать план. Actual time - реальные миллисекунды выполнения. Ускоряем мы запрос по фактическому времени, а не по cost. Большое расхождение между ожидаемыми и фактическими строками (rows) - первый сигнал, что у планировщика устаревшая статистика и пора делать ANALYZE таблицы.

SQL и примеры

Сначала правильно снимаем план. Три настройки делают его пригодным для глубокого explain analyze разбора:

Код: Выделить всё

SET track_io_timing = TRUE;

EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM flights WHERE flight_no = 'PG0001';
Что тут что. ANALYZE заставляет PostgreSQL реально выполнить запрос и показать фактические строки и время. BUFFERS добавляет статистику буферов (попадания в кэш и чтения с диска). track_io_timing = TRUE включает измерение реального времени ввода-вывода, и тогда в плане появляются строки I/O Timings. В PostgreSQL 18 BUFFERS включается вместе с ANALYZE автоматически, дописывать его уже не нужно - но привычка не повредит.

Осторожно с пишущими запросами. ANALYZE действительно выполняет INSERT, UPDATE, DELETE. Чтобы снять план и не менять данные, оборачивайте в транзакцию с откатом:

Код: Выделить всё

BEGIN;
EXPLAIN (ANALYZE, BUFFERS)
DELETE FROM boarding_passes WHERE ticket_no = '0005432000284';
ROLLBACK;
Какие форматы примет explain.tensor.ru. Обычный текстовый вывод из psql, формат pgAdmin (со скобками и кавычками вокруг строк), JSON через EXPLAIN (FORMAT JSON ...) и вариант с COSTS OFF. План можно вставить копипастом или перетащить файлом. Там же есть бьютифайер SQL и публичный архив разобранных планов.

Теперь главное - какие узлы вы увидите в дереве и что они значат:
  • Seq Scan - последовательное чтение всей таблицы. По большой таблице без индекса это частый виновник торможения.
  • Index Scan - проход по индексу плюс обращение к таблице за остальными колонками.
  • Index Only Scan - данные взяты только из индекса, в таблицу не ходим. Самый дешёвый доступ.
  • Bitmap Index Scan + Bitmap Heap Scan - пара для выборки многих строк: сначала строим битовую карту страниц по индексу, потом читаем страницы пачкой.
  • Nested Loop - для каждой строки внешнего набора ищем совпадения во внутреннем. Хорош на малых объёмах, опасен при большом числе loops.
  • Hash Join - строим хэш по меньшей таблице, прогоняем большую. Рабочая лошадка для крупных соединений.
  • Merge Join - слияние двух отсортированных потоков.
  • Sort, Aggregate, Gather - сортировка, агрегация и сбор результатов параллельных воркеров.
Как читать строки узла. Возьмём типичную строку: (cost=0.00..1.10 rows=10) (actual time=0.02..0.05 rows=9 loops=3). Фактическое число обработанных строк = rows на узле, умноженное на loops. Здесь это 9 * 3 = 27. Эта арифметика критична внутри Nested Loop: видите rows=9, а внутренний узел крутится loops=50000 - вот ваше узкое место.

Про BUFFERS детально, это часто недооценивают. shared hit - страницы взяты из кэша shared_buffers, это быстро. shared read - страницы читались мимо кэша, с диска или из кэша ОС, вот оно реальное I/O. local - буферы временных таблиц. temp - временные данные на диске для сортировок, хэшей и Materialize. Если у узла Sort стоит external merge Disk и большой temp written - значит не хватило work_mem и сортировка ушла на диск. Если у скана огромный shared read - узел упирается в чтение с диска.

Частые грабли
  • Снимают EXPLAIN без ANALYZE и спорят про оценки планировщика как про реальность. Без ANALYZE нет фактического времени вообще.
  • Ищут узкое место по самому большому cost. Ориентир - фактическое время узла, сервис не зря подсвечивает долю времени.
  • Забывают про loops в Nested Loop и пугаются маленького rows, не заметив тысячи итераций.
  • Не сделали SET track_io_timing = TRUE и удивляются, что нет реального времени I/O в плане.
  • Гонят EXPLAIN ANALYZE на боевом UPDATE без BEGIN/ROLLBACK и меняют данные.
  • Видят расхождение оценки и факта в сотни раз и лезут крутить индексы, хотя сначала надо ANALYZE таблицы - устаревшая статистика или коррелированные условия врут планировщику.
  • Меряют первый прогон холодного запроса: shared read большой просто потому, что кэш пуст, второй прогон даёт shared hit. Сравнивайте сопоставимое.
Мини-лаба
  • Подключитесь к демобазе Авиаперевозки в psql и выполните SET track_io_timing = TRUE;
  • Снимите план соединения без индекса: EXPLAIN (ANALYZE, BUFFERS) SELECT t.passenger_name, f.flight_no FROM tickets t JOIN ticket_flights tf ON tf.ticket_no = t.ticket_no JOIN flights f ON f.flight_id = tf.flight_id WHERE f.flight_no = 'PG0007';
  • Скопируйте весь вывод, откройте explain.tensor.ru и вставьте план. Найдите подсвеченный самый тяжёлый узел и его долю времени.
  • Посмотрите, есть ли Seq Scan по flights и каков у него shared read.
  • Создайте индекс: CREATE INDEX ON flights (flight_no); затем выполните ANALYZE flights;
  • Снимите план повторно и снова загрузите в сервис.
  • Сравните total actual time, тип скана по flights и буферы до и после. Запишите, во сколько раз ускорилось.
Контрольные вопросы
  • Чем cost отличается от actual time и по какому из них искать узкое место?
  • Как из rows и loops получить реальное число обработанных строк на узле?
  • Что означают shared hit и shared read и как по ним найти I/O-проблему?
  • Почему EXPLAIN ANALYZE на INSERT/UPDATE/DELETE надо оборачивать в транзакцию с ROLLBACK?
  • О чём говорит сильное расхождение оценки rows и факта, и что делать в первую очередь?
  • Что изменилось в поведении BUFFERS в PostgreSQL 18?
👍4 ❤️1 🔥1 😄 🤔2
Аватара пользователя
arjona
Сообщения: 1
Зарегистрирован: 21 май 2026, 18:59

Re: Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE

Сообщение arjona »

А если запрос пишущий, но в нём подзапрос с CTE который тоже что-то меняет - ROLLBACK же всё откатит, я ничего не сломаю на проде? просто страшно EXPLAIN ANALYZE на боевом UPDATE запускать
👍1 ❤️1 🔥1 😄 🤔
Аватара пользователя
chan_grisha
Сообщения: 1
Зарегистрирован: 21 май 2026, 13:18

Re: Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE

Сообщение chan_grisha »

Поймал на демобазе: после CREATE INDEX план не поменялся, всё ещё Seq Scan по flights. Сделал ANALYZE flights - и сразу Index Scan. Реально без свежей статистики планировщик индекс игнорит, спасибо что отдельным шагом в лабе вынесли
👍1 ❤️1 🔥1 😄 🤔
Ответить
← Предыдущая глава
Внешние данные, расширения и сертификация

Все главы курса «PostgreSQL: от первого запроса до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: оконные функции postgresql примеры over partition byкак читать план запроса explain analyze в postgresql

Вернуться в «PostgreSQL: от первого запроса до продакшена»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость