Медленный SQL — одна из самых частых причин деградации backend-сервисов. Хорошая новость: в большинстве случаев проблему можно найти и исправить без переписывания всей системы. Базовый набор для диагностики — EXPLAIN, индексы и понимание работы планировщика.
1. Начинайте с EXPLAIN 🔍
EXPLAIN показывает, как СУБД собирается выполнять запрос: какие таблицы читать, какие индексы использовать, где делать сортировку, соединения и сколько строк ожидается на каждом этапе.
Что важно смотреть:
- type / access method — full scan или index scan
- rows — сколько строк планировщик ожидает прочитать
- key / index used — какой индекс выбран
- Extra / filters — есть ли filesort, temporary table, post-filtering
Если запрос читает миллионы строк ради десятков результатов — это явный сигнал к оптимизации.
2. Индексы ускоряют чтение, но не всё подряд 🧩
Индекс полезен, когда он помогает быстро найти нужные строки, а не сканировать таблицу целиком.
Когда индекс особенно нужен:
- поля в WHERE
- столбцы для JOIN
- поля в ORDER BY и иногда GROUP BY
- часто используемые фильтры по диапазону: дата, статус, category_id
Частые ошибки:
- индекс на колонку с низкой селективностью, например gender или is_active
- слишком много индексов — ускоряют SELECT, но замедляют INSERT/UPDATE/DELETE
- неправильный порядок полей в составном индексе
Пример: для запроса
WHERE user_id = ? AND created_at > ?
обычно лучше индекс (user_id, created_at), а не наоборот.
3. Планировщик не “ошибается”, он выбирает по статистике 🤖
Планировщик строит план выполнения на основе статистики таблиц и индексов. Если статистика устарела, он может выбрать неэффективный путь.
Что это значит на практике:
- после массовых изменений данных обновляйте статистику
- проверяйте, насколько реальная выборка совпадает с оценкой в плане
- не удивляйтесь, если один и тот же запрос на dev и prod работает по-разному
Иногда СУБД игнорирует индекс не потому, что “не умеет”, а потому что считает full scan дешевле.
4. Что чаще всего тормозит запросы 🚨
- SELECT * вместо выборки нужных полей
- функции над индексируемым полем: LOWER(email), DATE(created_at)
- LIKE '%text%' без специальных индексов
- лишние JOIN
- подзапросы, которые можно переписать в JOIN или CTE
- сортировка больших выборок без индекса
5. Практический алгоритм оптимизации ✅
- Запустите EXPLAIN
- Найдите full scan, expensive join, sort
- Проверьте фильтры и порядок условий
- Добавьте или пересоберите индекс
- Уберите лишние поля и JOIN
- Сравните план и время до/после
Главное правило: оптимизируйте по фактам, а не по интуции. Сначала план выполнения, потом изменения в запросе или индексах. Именно такой подход дает стабильный прирост производительности без лишней магии ⚡
👀 В конце дня полезно держать под рукой качественные источники — посмотрите подборку каналов про IT: там регулярно выходят материалы по SQL, backend и производительности.