> Как проверить использование индексов и построение плана выполнения запроса в базе данных? (PHP)
Уровень: senior · Роль: backend · Язык: PHP · Категория: Технические вопросы
Компании: витринатв
Стек: PHP
> Пример ответа
Короткий ответ
В PostgreSQL и MySQL для проверки использования индексов используется команда EXPLAIN (или EXPLAIN ANALYZE в PostgreSQL). Она показывает план выполнения запроса: порядок сканирования таблиц, тип соединений, оценку количества строк и используемые индексы. В MySQL также доступен EXPLAIN FORMAT=JSON для детального вывода. Ключевые поля: type (должен быть ref, range, index, а не ALL), key (какой индекс выбран), rows (оценка строк). Для диагностики реального времени - EXPLAIN ANALYZE в PostgreSQL и EXPLAIN ANALYZE в MySQL 8.0+.
Подробное объяснение
План выполнения - это дерево операций, которые СУБД выполнит для получения результата. Проверка использования индексов сводится к анализу этого дерева.
Основные инструменты:
EXPLAIN- показывает план без выполнения. Даёт оценку стоимости, но не фактические данные.EXPLAIN ANALYZE- выполняет запрос и возвращает реальное время и количество строк на каждом шаге. В PostgreSQL это стандарт, в MySQL - начиная с 8.0.18.EXPLAIN (FORMAT JSON)- структурированный вывод для автоматического анализа.
Что смотреть в выводе:
-
Тип сканирования (
typeв MySQL,Node Typeв PostgreSQL):ALL(MySQL) /Seq Scan(PostgreSQL) - полное сканирование таблицы, индекс не используется.index- полное сканирование индекса (лучше, чемALL, но не идеально).range- сканирование диапазона по индексу.ref/eq_ref- поиск по неуникальному/уникальному ключу.const- константный поиск по первичному ключу.
-
Поле
key- какой индекс реально выбран. ЕслиNULL- индекс не используется. -
rows- оценка количества строк, которые будут прочитаны. Если значение близко к общему количеству строк в таблице - индекс неэффективен. -
Extra(MySQL) /Filter(PostgreSQL) - наличиеUsing whereбезUsing indexможет указывать на то, что фильтрация идёт после чтения строк, а не по индексу. -
Порядок соединений - в MySQL
EXPLAINпоказывает порядок таблиц вidиselect_type. В PostgreSQL - дерево сNested Loop,Hash Join,Merge Join.
Почему индекс может не использоваться:
- Функция или выражение над колонкой в
WHERE(например,WHERE YEAR(created_at) = 2024). - Неявное приведение типов (сравнение
VARCHARс числом). LIKE '%pattern'- индекс не работает для ведущего wildcard.- Низкая селективность - оптимизатор решает, что дешевле прочитать всю таблицу.
- Устаревшая статистика - нужно выполнить
ANALYZE TABLE(MySQL) илиANALYZE(PostgreSQL).
На практике
Для PHP-разработчика важно уметь быстро диагностировать медленные запросы. Типичный workflow:
- Включить логирование медленных запросов (slow query log) в MySQL или
auto_explainв PostgreSQL. - Найти проблемный запрос.
- Выполнить
EXPLAIN ANALYZEс реальными параметрами. - Проверить, какие индексы существуют (
SHOW INDEX FROM tableв MySQL,\d+ tableв PostgreSQL). - Если индекс не используется - понять причину (функция, тип, селективность).
- Попробовать переписать запрос или добавить композитный индекс.
В PHP-проектах часто используют ORM (Doctrine, Eloquent). Важно уметь получить SQL из ORM и прогнать его через EXPLAIN. Например, в Doctrine: $query->getSQL(), в Eloquent: $query->toSql().
Полезные практики:
- Для PostgreSQL использовать
EXPLAIN (ANALYZE, BUFFERS)- покажет чтение из кэша и диска. - Для MySQL -
EXPLAIN FORMAT=JSONи обращать внимание наattached_conditionиused_key_parts. - Проверять план на репрезентативных данных - на пустой таблице оптимизатор может выбрать
Seq Scan, а на заполненной - индекс.
Пример кода
SQL-- PostgreSQL: реальный план с временемEXPLAIN (ANALYZE, BUFFERS)SELECT * FROM usersWHERE email = 'test@example.com';-- MySQL 8+: JSON-планEXPLAIN FORMAT=JSONSELECT * FROM usersWHERE email = 'test@example.com';-- MySQL: классический выводEXPLAIN SELECT * FROM usersWHERE email = 'test@example.com';
Пример вывода PostgreSQL:
Index Scan using idx_users_email on users (cost=0.43..8.45 rows=1 width=32) (actual time=0.015..0.016 rows=1 loops=1) Index Cond: (email = 'test@example.com'::text) Buffers: shared hit=3 Planning Time: 0.045 ms Execution Time: 0.028 ms
Пример вывода MySQL:
+----+-------------+-------+------------+------+------------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+------------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | users | NULL | ref | idx_users_email | idx_users_email | 1023 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+------------------+---------+---------+-------+------+----------+-------+
В PHP-коде можно автоматизировать проверку:
PHP$sql = "EXPLAIN ANALYZE " . $query->toSql();$result = $pdo->query($sql)->fetchAll();// Анализ $result: проверка Node Type, Index Name, Actual Rows
Как отвечать на собеседовании
Начните с EXPLAIN как основного инструмента. Затем уточните разницу между EXPLAIN и EXPLAIN ANALYZE - первый показывает план, второй - реальное выполнение. Упомяните, что в MySQL и PostgreSQL синтаксис и поля отличаются.
Покажите понимание, что индекс - не панацея: оптимизатор может отказаться от него при низкой селективности или из-за функций в WHERE. Приведите пример, когда индекс не сработает: WHERE DATE(created_at) = '2024-01-01'.
Расскажите про композитные индексы и правило левостороннего префикса. Например, индекс (user_id, created_at) эффективен для WHERE user_id = ? ORDER BY created_at, но не для WHERE created_at = ?.
Упомяните практические инструменты: pg_stat_statements в PostgreSQL, performance_schema в MySQL, EXPLAIN ANALYZE для реальных данных.
Завершите мысль о том, что проверка плана - это итеративный процесс: изменили индекс → пересмотрели план → проверили на реальной нагрузке.
Что проверяет интервьюер
Интервьюер оценивает:
- Знание синтаксиса
EXPLAINв конкретной СУБД (важно уточнить, о какой БД речь). - Умение читать план: отличать
Seq ScanотIndex Scan, пониматьNested LoopvsHash Join. - Понимание причин, по которым индекс не используется.
- Знание композитных индексов и порядка колонок.
- Практический опыт: как вы находите и исправляете медленные запросы в реальном проекте.
- Умение объяснить trade-off: индекс ускоряет чтение, но замедляет запись и занимает место.
Для senior-позиции важно показать системный подход: не просто "выполнил EXPLAIN", а "проанализировал статистику, проверил селективность, переписал запрос или изменил индекс".
Типичные ошибки
- Путаница между
EXPLAINиEXPLAIN ANALYZE- первый не выполняет запрос, поэтому не показывает реальное время. - Игнорирование
rows- если оценка строк большая, индекс неэффективен, даже еслиkeyзаполнен. - Непонимание, что
type = indexв MySQL - это не использование индекса для поиска, а полное сканирование индекса. - Уверенность, что индекс всегда ускоряет запрос - на маленьких таблицах
Seq Scanбыстрее. - Забывают про неявное приведение типов:
WHERE varchar_col = 123- индекс не сработает. - Не проверяют план на реальных данных - на пустой таблице оптимизатор ведёт себя иначе.
- В MySQL не смотрят на
filtered- если 10%, то оптимизатор прочитает много лишних строк. - Не учитывают, что ORM может генерировать неоптимальный SQL - нужно смотреть именно итоговый запрос.
> Похожие задачи по PHP
Для чего нужна иммутабельность
Какие минусы использования магических методов в PHP
Какие угрозы безопасности существуют при работе с данными в SQL
Какой базовый принцип безопасности при работе с пользовательскими данными
> Похожие задачи по backend
Для чего нужна иммутабельность
Какие минусы использования магических методов в PHP
Какие угрозы безопасности существуют при работе с данными в SQL
Какой базовый принцип безопасности при работе с пользовательскими данными
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью