SQL

БЛОК 8. Производительность — 22. EXPLAIN и план запроса

📚 24 вопросовПройти тест →
Лекция

БЛОК 8. Производительность — 22. EXPLAIN и план запроса

SQL

22. EXPLAIN и план запроса

🧭 Введение: почему EXPLAIN важнее «ощущения, что стало медленно»

Когда SQL начинает тормозить, команда часто спорит «вроде индекс есть», «вроде запрос простой».
Проблема в том, что без плана выполнения мы обсуждаем догадки, а не факты.
В этой теме разберём базовый production-фундамент по EXPLAIN:
  • что такое план запроса;
  • как различать Seq Scan и Index Scan;
  • как понять, что запрос реально медленный;
  • как читать базовый вывод EXPLAIN на практическом примере.
💡 Совет: Перед оптимизацией сначала смотрите план, потом меняйте запрос или индексы.
Вывод: EXPLAIN переводит разговор о производительности из «мнений» в инженерные факты.

⚠️ Проблема -> решение

Типичная проблема:
  1. запрос иногда отвечает быстро, иногда очень медленно;
  2. индексы добавляют «на всякий случай», но поведение почти не меняется;
  3. команда не понимает, где именно узкое место: скан, сортировка или джойн.
Решение:
  1. зафиксировать медленный запрос и его параметры;
  2. посмотреть план через EXPLAIN;
  3. сопоставить план с данными (Seq Scan/Index Scan, объём строк, сортировки);
  4. только после этого менять SQL или индексы.
🟢 Если совсем просто: Нет плана -> нет понимания, почему медленно.
🎯 Как понять, что этап прошёл успешно: Вы можете показать план и объяснить, где именно запрос тратит время.

🛠️ Чем помогает и как работает

EXPLAIN показывает, какой путь доступа к данным выбрала БД.
Это центральный инструмент для любых решений по индексации и переписыванию запросов.
🟢 Если совсем просто: EXPLAIN показывает маршрут запроса внутри БД.
🎯 Как понять, что этап прошёл успешно: Вы отличаете «планируемое поведение» от «фактического узкого места».
Чем помогает:
  • показывает, где выбран Seq Scan, а где Index Scan;
  • позволяет увидеть дорогие сортировки и соединения;
  • помогает понять, почему индекс не используется;
  • даёт основу для точечной оптимизации.
Как это работает:
  • Шаг 1: БД разбирает SQL и оценивает варианты выполнения;
  • Шаг 2: планировщик считает стоимость каждого варианта;
  • Шаг 3: выбирается «самый дешёвый» путь по модели стоимости;
  • Шаг 4: EXPLAIN выводит этот план в читаемом виде.
Вывод: Производительность запроса начинается с чтения плана, а не с случайных изменений в схеме.

📚 Ключевые термины (простыми словами)

Перед практикой синхронизируем словарь.
🟢 Если совсем просто: Без терминов сложно обсуждать, почему запрос медленный.
🎯 Как понять, что этап прошёл успешно: Вы уверенно объясняете plan, Seq Scan, Index Scan, cost, rows.
  • Query plan (план запроса) — последовательность операций, которые БД выполнит для получения результата.
  • Planner/Optimizer (планировщик) — компонент БД, выбирающий лучший путь выполнения.
  • Seq Scan — последовательное чтение таблицы целиком.
  • Index Scan — чтение через индекс с доступом к части строк.
  • Cost — оценка стоимости узла плана (не миллисекунды, а внутренняя модель БД).
  • Rows — ожидаемое число строк на узле.
  • Actual Rows / Actual Time — фактические показатели выполнения (в EXPLAIN ANALYZE).
  • Filter — условие, которым БД отбрасывает строки.
  • Sort — отдельная операция сортировки, если индекс не покрывает нужный порядок.
Вывод: Эти термины закрывают базовую инженерную коммуникацию о планах запросов.

🧩 1. Что такое план запроса

План запроса — это не «технический шум», а объяснение, как БД реально получит ваши данные.
Один и тот же SQL может иметь разный план при изменении объёма таблицы, индексов и статистики.
🟢 Если совсем просто: План = пошаговый маршрут выполнения SQL.
🎯 Как понять, что этап прошёл успешно: Вы можете показать, какие операции выполняются и в каком порядке.
Назначение: Понять внутренний путь выполнения запроса до начала оптимизации.
Простыми словами: БД сначала решает «как идти», и только потом выполняет SQL.
Для новичка: Не пытайтесь ускорять запрос, пока не увидели, какой путь выбрала БД.
Аналогия: Это как навигатор: одинаковая точка назначения, но маршруты могут быть разными.
Пример:
EXPLAINSELECT  u.id,  u.emailFROM users uWHERE u.email = 'alice@example.com';
🔎 Как это происходит на практике:
  • Контекст: запрос выглядит коротким, но иногда отвечает медленно.
  • Действия: смотрим план и фиксируем выбранные узлы.
  • Результат: получаем объективную картину, прежде чем трогать код.
Характеристики:
  • план зависит от индексов и статистики;
  • короткий SQL не гарантирует короткий путь выполнения;
  • у большого запроса может быть эффективный план, у простого — нет.
Когда использовать: Для каждого hot-path запроса в API и фоновых джобах.
Вывод: План запроса — обязательная точка входа в любую оптимизацию SQL.

🔎 2. Seq Scan и Index Scan

Это два базовых узла, которые нужно уметь распознавать с первого взгляда.
Именно здесь чаще всего виден корень проблемы «почему медленно».
🟢 Если совсем просто: Seq Scan читает всё, Index Scan читает нужную часть.
🎯 Как понять, что этап прошёл успешно: Вы различаете случаи, где Seq Scan нормален, и где он сигнализирует о проблеме.
Назначение: Понимать тип доступа к данным и его влияние на время ответа.
Простыми словами: Если БД читает всю таблицу ради нескольких строк, это обычно дорого.
Для новичка: Seq Scan не всегда плохо: на маленьких таблицах он может быть дешевле индекса.
Пример (Seq Scan):
EXPLAINSELECT  u.id,  u.is_activeFROM users uWHERE u.is_active = true;
Пример (Index Scan):
EXPLAINSELECT  o.id,  o.user_idFROM orders oWHERE o.user_id = 42;
🔎 Как это происходит на практике:
  • Контекст: один запрос «съедает» ресурсы на проде.
  • Действия: проверяем, чем он читает данные — полным сканом или индексом.
  • Результат: понимаем, нужна ли работа с индексом/фильтром.
Характеристики:
  • Seq Scan часто появляется при низкой селективности;
  • Index Scan обычно эффективен для точных фильтров и узких диапазонов;
  • выбор делается планировщиком по стоимости, а не «по желанию разработчика».
Когда использовать: При первичной диагностике любого медленного запроса.
Вывод: Разбор Seq Scan и Index Scan — первый практический шаг к осмысленной оптимизации.

🚦 3. Как понять, что запрос медленный

«Медленный» — это не абсолютное число, а отклонение от целевого SLA сценария.
Один и тот же запрос может быть нормой для nightly job и проблемой для API-эндпоинта.
🟢 Если совсем просто: Медленный запрос — тот, который не укладывается в ожидания вашего сценария.
🎯 Как понять, что этап прошёл успешно: Вы можете назвать порог и показать факт превышения.
Назначение: Отделить субъективное «кажется медленным» от измеримого инцидента.
Простыми словами: Нужен целевой порог: например, «эндпоинт должен отвечать до 200 мс».
Для новичка: Смотрите не только среднее время, но и хвосты (p95, p99).
Пример:
EXPLAIN ANALYZESELECT  o.id,  o.status,  o.created_atFROM orders oWHERE o.status = 'paid'ORDER BY o.created_at DESC, o.id DESCLIMIT 20;
🔎 Как это происходит на практике:
  • Контекст: пользователи жалуются на «подвисание» списка.
  • Действия: фиксируем SLA и проверяем фактическое время запроса.
  • Результат: понимаем, есть ли реальный перфоманс-баг и насколько он критичен.
Характеристики:
  • «медленно» всегда привязано к бизнес-сценарию;
  • важны tail-метрики, а не только среднее;
  • без EXPLAIN ANALYZE сложно понять, где именно тратится время.
Когда использовать: При расследовании деградации производительности и в performance-review.
Вывод: Сначала определите целевой порог, затем проверяйте факт через измерения.

🧪 4. Базовый пример EXPLAIN

Этот блок нужен, чтобы снять страх перед первым чтением плана.
Вам не нужно знать все узлы: достаточно видеть тип скана, фильтры и порядок операций.
🟢 Если совсем просто: Базовый EXPLAIN уже даёт достаточно сигналов для первых решений.
🎯 Как понять, что этап прошёл успешно: Вы можете прочитать план и коротко объяснить его коллеге.
Назначение: Научиться делать первый практический разбор плана.
Простыми словами: Сначала смотрим структуру плана, потом углубляемся в детали.
Для новичка: Начните с вопроса: «Где БД читает много лишних строк?»
Пример:
EXPLAINSELECT  o.id,  o.total_amountFROM orders oWHERE o.user_id = 42  AND o.status = 'paid'ORDER BY o.created_at DESCLIMIT 20;
      
🔎 Как это происходит на практике:
  • Контекст: запрос должен вернуть «последние 20 заказов пользователя».
  • Действия: смотрим базовый план и ищем основные операции.
  • Результат: видим, где срабатывает индекс, а где остаётся фильтрация/сортировка.
Характеристики:
  • Limit показывает, что запросу нужны верхние N строк;
  • Sort сигнализирует о дополнительной операции сортировки;
  • Index Cond и Filter показывают, что именно использовано индексом, а что фильтруется после.
Когда использовать: На первом этапе диагностики, даже до ANALYZE.
Вывод: Базовый EXPLAIN уже помогает найти направление оптимизации.

⚙️ 5. EXPLAIN vs EXPLAIN ANALYZE (базово)

Для production-решений важно различать «планировал» и «реально выполнил».
EXPLAIN показывает намерение планировщика, EXPLAIN ANALYZE — фактическое поведение.
🟢 Если совсем просто: EXPLAIN — теория, EXPLAIN ANALYZE — практика.
🎯 Как понять, что этап прошёл успешно: Вы умеете сопоставлять rows и actual rows и делать выводы.
Назначение: Проверять гипотезы оптимизации на фактических данных.
Простыми словами: Оптимизируем по факту, а не по ожиданиям.
Для новичка: Если оценки и факт сильно расходятся, проверьте статистику и форму фильтра.
Пример:
EXPLAIN ANALYZESELECT  p.id,  p.nameFROM products pWHERE p.name LIKE 'iph%';
🔎 Как это происходит на практике:
  • Контекст: индекс есть, но запрос периодически тормозит.
  • Действия: сравниваем оценку строк и фактическое выполнение.
  • Результат: понимаем, ошибка в плане, в индексе или в самой форме запроса.
Характеристики:
  • EXPLAIN безопасен как «предпросмотр»;
  • EXPLAIN ANALYZE исполняет запрос и показывает факт;
  • для UPDATE/DELETE применяйте ANALYZE аккуратно, особенно в проде.
Когда использовать: После базового EXPLAIN, когда нужно подтвердить реальный эффект.
Вывод: Рабочий цикл оптимизации: EXPLAIN -> гипотеза -> EXPLAIN ANALYZE.

🧰 6. Мини-алгоритм диагностики медленного SQL

Чтобы не метаться между «добавим индекс» и «перепишем запрос», нужен короткий чек-лист.
Он помогает быстро перейти от симптома к конкретному инженерному действию.
🟢 Если совсем просто: Один и тот же порядок шагов экономит часы расследования.
🎯 Как понять, что этап прошёл успешно: Вы можете пройти путь от жалобы пользователя до конкретного плана исправления.
Назначение: Систематизировать работу с производительностью запроса.
Простыми словами: Сначала измеряем, потом меняем, потом снова измеряем.
Для новичка: Не меняйте и запрос, и индексы одновременно — иначе сложно понять, что реально помогло.
Пример алгоритма:
     
🔎 Как это происходит на практике:
  • Контекст: регулярные жалобы на медленный endpoint.
  • Действия: команда проходит единый диагностический pipeline.
  • Результат: решения становятся воспроизводимыми и проверяемыми.
Характеристики:
  • минимизирует хаотичные правки;
  • снижает риск ложных «улучшений»;
  • ускоряет code review и постмортемы.
Когда использовать: Для всех hot-path запросов и при любом регрессе производительности.
Вывод: Структурированный процесс диагностики важнее разовых «магических» оптимизаций.

🆚 Сравнение: Seq Scan vs Index Scan

КритерийSeq ScanIndex Scan
Что читаетОбычно всю таблицуПодмножество строк по индексу
Когда часто полезенМаленькие таблицы, широкие выборкиТочные фильтры, узкие диапазоны
Риск на больших таблицахДорогой полный проходВозможны дополнительные чтения таблицы
Типичный сигнал в планеSeq Scan on ...Index Scan using ...
Нужно ли «всегда избегать»НетТоже нет: оцениваем по факту
Вывод: Ни один тип скана не «хороший всегда» — важна уместность в конкретном запросе.

🧠 Must-Know (запомнить)

  • План запроса показывает реальный путь выполнения SQL.
  • EXPLAIN — первый шаг перед любой оптимизацией.
  • Seq Scan не всегда ошибка, особенно на маленьких таблицах.
  • Index Scan не гарантируется даже при наличии индекса.
  • Медленность запроса оценивают относительно SLA сценария.
  • Для фактических измерений используйте EXPLAIN ANALYZE.
  • Сравнивайте оценку (rows) и факт (actual rows).
  • Сильное расхождение оценки и факта — повод проверить статистику и форму фильтра.
  • Сначала делайте одно изменение, потом повторно измеряйте.
  • Оптимизация без плана почти всегда превращается в угадывание.
Вывод: Главный навык в теме — читать план и принимать решения по данным, а не по интуиции.

❌ Частые мифы

Миф: Если запрос короткий, он точно быстрый. ✅ Как правильно: Короткий SQL может иметь тяжёлый план на больших данных. 📎 Почему это важно: Синтаксис и производительность — разные уровни.
Миф: Seq Scan всегда плохо. ✅ Как правильно: Для маленьких таблиц и широких выборок это может быть нормальным и дешёвым путём. 📎 Почему это важно: Не нужно оптимизировать «по названию узла».
Миф: Если индекс есть, БД обязана его использовать. ✅ Как правильно: Планировщик выбирает путь по стоимости. 📎 Почему это важно: Лишний индекс без изменения плана не решает проблему.
Миф: Достаточно один раз посмотреть EXPLAIN. ✅ Как правильно: После изменений нужен повторный замер (EXPLAIN ANALYZE). 📎 Почему это важно: Иначе сложно доказать, что оптимизация реально помогла.

🎤 Часто спрашивают на собеседованиях

Вопрос: Что такое план запроса? ✅ Ответ: Это набор операций, которыми БД получает результат SQL.
Вопрос: В чём разница между Seq Scan и Index Scan? ✅ Ответ: Seq Scan читает таблицу последовательно, Index Scan использует индекс для доступа к части строк.
Вопрос: Как понять, что запрос действительно медленный? ✅ Ответ: Сравнить фактическое время запроса с целевым SLA и проверить хвостовые метрики.
Вопрос: Когда Seq Scan может быть нормальным? ✅ Ответ: На маленьких таблицах или когда запрос возвращает большую долю строк.
Вопрос: Что показывает EXPLAIN, а что EXPLAIN ANALYZE? ✅ Ответ: EXPLAIN показывает план, EXPLAIN ANALYZE выполняет запрос и показывает фактические метрики.
Вопрос: Почему индекс может не использоваться? ✅ Ответ: Потому что по оценке стоимости полный скан может оказаться дешевле.
Вопрос: Что смотреть в плане в первую очередь? ✅ Ответ: Типы сканов, узлы сортировки/джойнов, ожидаемое и фактическое число строк.
Вопрос: Какой минимальный цикл оптимизации запроса? ✅ Ответ: EXPLAIN -> одно изменение -> EXPLAIN ANALYZE -> сравнение результата.

🚨 Типичные ошибки

  • Оптимизировать запрос без чтения плана.
  • Путать cost с миллисекундами.
  • Считать любой Seq Scan багом.
  • Игнорировать EXPLAIN ANALYZE и смотреть только теоретический план.
  • Менять сразу SQL и индексы, не фиксируя эффект по шагам.
  • Не учитывать SLA сценария и оптимизировать «в вакууме».
  • Делать выводы по одному запуску без повторной проверки.
Вывод: Большинство провалов в оптимизации возникает из-за отсутствия дисциплины измерений.

✅ Best Practices

  • Для каждого критичного запроса храните эталонный EXPLAIN.
  • Сначала фиксируйте симптом и SLA, затем анализируйте план.
  • Начинайте чтение плана с типа скана и объёма строк.
  • После любого изменения проверяйте EXPLAIN ANALYZE.
  • Делайте одну оптимизацию за раз, чтобы видеть причинно-следственную связь.
  • Используйте реальные параметры и данные, близкие к прод-нагрузке.
  • Документируйте, какой узел был узким местом и как его исправили.
Вывод: Production-ready работа с производительностью — это процесс измерения и верификации.

🏁 Заключение

EXPLAIN и понимание плана запроса — базовый навык SQL-инженерии.
Если вы различаете Seq Scan и Index Scan, умеете формально определить «медленный запрос» и читать базовый вывод плана, вы уже можете осознанно улучшать производительность без хаотичных правок.
Вывод: Оптимизировать SQL нужно через план и фактические метрики, а не через догадки.
🎯

Проверьте знания

Закрепите материал — пройдите тест по теме «БЛОК 8. Производительность — 22. EXPLAIN и план запроса»

Пройти тест →