Ответ
EXPLAIN (или EXPLAIN ANALYZE в PostgreSQL) — это основной инструмент для анализа плана выполнения запроса и поиска узких мест. Вот на что я смотрю в первую очередь:
Ключевые столбцы в выводе EXPLAIN (на примере MySQL):
-
type(самый важный): Тип соединения. От него сильно зависит производительность.system/const: Идеально. Запрос находит одну строку по первичному или уникальному ключу.eq_ref: Отлично. Используется приJOINпо первичному ключу.ref: Хорошо. Использование не-уникального индекса для поиска.range: Нормально. Поиск по диапазону индекса (BETWEEN,IN,>).index: Плохо. Полное сканирование индекса (индексный файл читается целиком).ALL: Очень плохо. Полное сканирование таблицы (table scan). Требует добавления индекса.
-
key: Какой индекс фактически используется. ЕслиNULL— индекс не используется, что часто ведет кALL. -
rows: Оценочное количество строк, которое движку БД придется проверить. Чем меньше, тем лучше. Резкое расхождение с реальным числом обработанных строк может указывать на устаревшую статистику. -
Extra: Дополнительная информация, критичная для оптимизации.Using index: Отлично (Covering Index). Все нужные данные есть в самом индексе, чтение таблицы не требуется.Using where: СУБД фильтрует строки после их чтения из таблицы или индекса.Using temporary: Плохо. Создается временная таблица (часто приGROUP BY,ORDER BYбез индекса).Using filesort: Плохо. Требуется дополнительная сортировка во внешней памяти.
Практический пример анализа:
EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 'shipped';
Возможный вывод и интерпретация:
| id | select_type | table | type | possible_keys | key | rows | Extra |
|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | orders | ref | idx_user,idx_status | idx_user | 50 | Using where |
type: ref— неплохо, используется индекс.key: idx_user— используется индекс поuser_id.rows: 50— будет проверено около 50 строк.Extra: Using where— условиеstatus = 'shipped'применяется после доступа по индексуuser_id.
Проблема: Индекс используется только по первому полю (user_id). Для 50 строк это может быть нормально, но если у пользователя тысячи заказов, фильтрация по status будет медленной.
Решение: Создать составной индекс, покрывающий оба поля:
CREATE INDEX idx_user_status ON orders(user_id, status);
После этого EXPLAIN может показать type: ref, key: idx_user_status, Extra: Using index (если запрос выбирает только эти два поля) — что является оптимальным планом.