22. EXPLAIN и план запроса
🧭 Введение: почему EXPLAIN важнее «ощущения, что стало медленно»
Когда SQL начинает тормозить, команда часто спорит «вроде индекс есть», «вроде запрос простой».
Проблема в том, что без плана выполнения мы обсуждаем догадки, а не факты.
Проблема в том, что без плана выполнения мы обсуждаем догадки, а не факты.
В этой теме разберём базовый production-фундамент по
EXPLAIN:- что такое план запроса;
- как различать
Seq ScanиIndex Scan; - как понять, что запрос реально медленный;
- как читать базовый вывод
EXPLAINна практическом примере.
💡 Совет:
Перед оптимизацией сначала смотрите план, потом меняйте запрос или индексы.
✅ Вывод:
EXPLAIN переводит разговор о производительности из «мнений» в инженерные факты.⚠️ Проблема -> решение
Типичная проблема:
- запрос иногда отвечает быстро, иногда очень медленно;
- индексы добавляют «на всякий случай», но поведение почти не меняется;
- команда не понимает, где именно узкое место: скан, сортировка или джойн.
Решение:
- зафиксировать медленный запрос и его параметры;
- посмотреть план через
EXPLAIN; - сопоставить план с данными (
Seq Scan/Index Scan, объём строк, сортировки); - только после этого менять 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.
🎯 Как понять, что этап прошёл успешно:
Вы можете показать, какие операции выполняются и в каком порядке.
Назначение:
Понять внутренний путь выполнения запроса до начала оптимизации.
Простыми словами:
БД сначала решает «как идти», и только потом выполняет 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-эндпоинта.
Один и тот же запрос может быть нормой для 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 Scan | Index 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 нужно через план и фактические метрики, а не через догадки.