План запроса в PostgreSQL: как читать и искать узкие места
В PostgreSQL 17 план показывает, как база собирается выполнить запрос, но не объясняет сам себя: оценочная стоимость — не миллисекунды, а последовательное сканирование не всегда ошибка. Разбираем, что запускать, на какие цифры смотреть и когда менять запрос или индекс.

В PostgreSQL 17 план запроса помогает найти, где теряются время и ресурсы: в чтении таблицы, соединении или неверной оценке количества строк. Чтобы не лечить запрос по одному слову «Seq Scan», сравните прогноз планировщика с фактическими данными через EXPLAIN ANALYZE и проверьте буферы. Цена ошибки — лишний индекс, который замедляет запись, или оптимизация, не меняющая время выполнения.
Сначала выберите безопасный способ просмотра
У EXPLAIN есть несколько режимов. Они отвечают на разные вопросы: какой план выбрала база, что произошло при выполнении и удобно ли обрабатывать результат программно. Для примеров ниже используется PostgreSQL 17; синтаксис сверьте с документацией своей основной версии.
| Режим | Что показывает | Цена и риск |
|---|---|---|
EXPLAIN | Предполагаемый план и оценки планировщика | Запрос не выполняется, поэтому нет фактического времени и количества строк |
EXPLAIN (ANALYZE, BUFFERS) | Фактическое выполнение, строки и статистику буферов | Запрос выполняется целиком и расходует ресурсы; для записи может менять данные |
EXPLAIN (FORMAT JSON) | План в формате, удобном для инструментов и сравнения | Нужен обработчик JSON; сами по себе данные не объясняют причину медленной работы |
Начинайте с обычного EXPLAIN, если запрос изменяет данные или выполнять его небезопасно. Для чтения производительности нужен ANALYZE: он запускает запрос, а не просто уточняет прогноз. Не проверяйте так разрушительный запрос в рабочей базе без понимания последствий. Транзакция с последующим откатом не отменяет все возможные побочные эффекты, например расход значений последовательности.
План — не рейтинг хороших и плохих операторов. Это объяснение выбора планировщика для конкретных данных, условий и настроек.
Снимите план на реальных условиях
Пусть нужно получить последние 50 заказов клиента. Пример предполагает, что в таблице orders есть поля id, customer_id и created_at. Замените таблицу, фильтр и параметры на свой запрос: план для другой формы запроса может быть совсем иным.
EXPLAIN (ANALYZE, BUFFERS, SETTINGS)
SELECT id, created_at
FROM orders
WHERE customer_id = 42
ORDER BY created_at DESC
LIMIT 50;
Запускайте запрос с теми же значениями параметров и сопоставимым объёмом данных, что в приложении. Если приложение использует подготовленные запросы, проверьте сценарий именно с ними: план для литерала и план с параметрами могут отличаться. Не копируйте чувствительные данные в сторонние анализаторы планов.
- Сравните оценку строк с фактом. В узле плана найдите
rows=иactual rows=. Большое расхождение часто важнее, чем название узла: планировщик мог неверно оценить, сколько записей пройдёт фильтр. Число фактических строк обычно указано в среднем за один запуск узла, аloopsпоказывает число запусков. - Проверьте, где потрачено время.
actual timeизмеряется в миллисекундах. Смотрите на узлы, которые выполняются долго или многократно; при большомloopsдаже короткая операция на каждом проходе может стать дорогой. Время дочерних узлов нельзя просто сложить: их работа входит в работу родительских узлов. - Разберите чтение буферов.
shared hitозначает, что блоки нашлись в общих буферах PostgreSQL;shared read— что их пришлось загрузить туда. Это не доказывает, что данные физически читали с диска: их мог отдать кеш операционной системы. Сравнивайте показатели вместе со временем и объёмом результата. - Сопоставьте оценки стоимости.
cost— внутренняя оценка планировщика в условных единицах, а не время в миллисекундах. Используйте её, чтобы понять, почему выбран один план, а не другой, но не обещайте по ней пользователю скорость ответа.
Термин: кардинальность — число строк, которое узел плана ожидает обработать или фактически обрабатывает. Ошибка в оценке кардинальности способна повлиять на выбор соединения и порядок выполнения операций.
Не лечите план по одному узлу
Seq Scan означает последовательное чтение таблицы, но не автоматически плохой план. Для небольшой таблицы или запроса, которому нужны многие строки, это может быть дешевле поиска по индексу с последующими чтениями таблицы. И наоборот, наличие индекса не гарантирует, что планировщик обязан его использовать.
Когда начинать с статистики
Если оценка строк сильно отличается от факта, сначала проверьте актуальность статистики. После массовой загрузки данных можно обновить её командой ANALYZE orders;. Это не перестраивает индексы и не переписывает таблицу, а собирает данные, на которых планировщик строит оценки. Для взаимосвязанных условий простого обновления иногда недостаточно: могут понадобиться расширенные статистики. Их тип и состав выбирают по конкретному запросу и данным, а не добавляют автоматически.
Когда проверять запрос и индекс
Если статистика актуальна, а запрос всё равно читает намного больше строк, чем возвращает, проверьте условия фильтрации, соединения и сортировку. Сначала убедитесь, что запросу действительно нужны все выбранные столбцы и строки. Затем оцените, поможет ли индекс именно этому сочетанию фильтра и сортировки. Индекс занимает место и добавляет работу при изменении данных, поэтому создавать его только ради исчезновения Seq Scan не стоит.
Что ломается при чтении плана
- Сравнивают только итоговое время. Оно зависит от нагрузки, кеша и параметров запуска. Снимайте планы в сопоставимых условиях и отмечайте версию PostgreSQL, объём данных и значения параметров.
- Считают
shared readпрямым чтением с диска. Это число блоков, загруженных в буферы PostgreSQL, а не показание физического накопителя. Для оценки дисковой нагрузки нужны дополнительные метрики системы. - Принимают каждый узел за отдельную последовательную операцию. Узлы вложены друг в друга; у параллельного плана есть дополнительные особенности. Читайте дерево целиком, включая отношения родителя и дочерних узлов.
- Проверяют запрос на нерепрезентативных данных. План на пустой тестовой таблице мало говорит о поведении на рабочем объёме и распределении значений.
- Добавляют индекс, не проверяя цену записи. Ускорение чтения может обернуться дополнительными затратами на вставку, обновление, хранение и обслуживание индекса.
Когда какой режим выбирать
Для безопасного первичного разбора используйте обычный EXPLAIN. Для поиска узкого места в чтении снимайте EXPLAIN (ANALYZE, BUFFERS) на репрезентативных параметрах, предварительно учитывая нагрузку. JSON выбирайте, если план нужно передать инструменту или сравнивать автоматически; для ручного разбора текстовый вывод обычно проще.
Если запрос записи всё же нужно анализировать, делайте это в тестовой среде с копией данных. В PostgreSQL можно обернуть изменение в транзакцию и откатить её, но это не универсальная защита от всех побочных эффектов. Учитывайте триггеры, внешние вызовы и другие действия, которые не обязаны откатываться вместе с транзакцией.
Что делать дальше
Сохраните план до изменения и после него, вместе с версией PostgreSQL, SQL-запросом без чувствительных данных и параметрами теста. Проверяйте не только время, но и фактические строки, количество запусков узлов и буферы. Если расхождение оценок повторяется, обновите статистику и снова снимите план; если нет — исследуйте конкретный фильтр, соединение или сортировку. Инвалидация кеша и диагностика нагрузки всей системы — отдельные темы.
Субтитры и таймкоды: Расшифровка речи и субтитры по правилам читаемости: 42 знака в строке, две строки, минимум секунда на реплику.
Спросить за 40 ₽Источники: PostgreSQL 17: Using EXPLAIN, PostgreSQL 17: EXPLAIN
Частые вопросы
Можно ли запускать EXPLAIN ANALYZE на рабочей базе?
Если в плане есть Seq Scan, обязательно нужен индекс?
Что делать, если rows и actual rows сильно различаются?
shared read означает, что PostgreSQL читал с диска?
Что изменится, если у меня другая версия PostgreSQL?
Почему запрос медленный, хотя план выглядит нормально?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.