Узнайте, как находить столбцы для индексации в SQL, измерять эффективность индексов и избегать лишних затрат. Практические советы с примерами кода.
Представьте: ваш эндпоинт, который работал мгновенно на маленькой базе, вдруг начал тайм-аутить в продакшене. Знакомая ситуация? Чаще всего проблема в отсутствии индекса. Я покажу, как по опыту находить столбцы, которые действительно нужно индексировать, как доказать, что индекс помог, и как не переплачивать за лишние.
Кандидаты — столбцы, которые вы фильтруете, сортируете или используете в JOIN. В терминах SQL — всё, что стоит в WHERE, ORDER BY, JOIN или GROUP BY на большой или растущей таблице.
user_id и order_id. В Rails они не получают индекс автоматически, если не указать опцию, а по ним постоянно идут ассоциации и JOIN. Это самый частый пропущенный индекс.email, slug, token. Обычно они должны иметь уникальный индекс.status, created_at — то, что используется в дашбордах и пагинации.Всё это неважно на маленьком объёме. Последовательное сканирование 500 строк — мгновенно. Проблемы начинаются на нескольких миллионах строк, когда эндпоинт, работавший в разработке, начинает тайм-аутить в продакшене.
Основной инструмент — EXPLAIN ANALYZE. Пример запроса:
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 42 AND status = 'paid';Что читать в выводе:
Сравнивайте фактическое время выполнения до и после. Также проверяйте оценённое количество строк против реального — если они сильно расходятся, планировщик работает на устаревшей статистике. Иногда простой ANALYZE исправляет запрос без индекса.
Локальный план — не всё. Чтобы найти, что стоит оптимизировать, смотрите на продакшен: pg_stat_statements для реально тяжёлых запросов, APM (New Relic, Skylight, Datadog) и тайминги запросов в логах. Легко увлечься оптимизацией запроса, который выполняется дважды в день, игнорируя тот, что летает 10 тысяч раз в час.
Каждый индекс занимает место на диске и замедляет запись: каждый INSERT, UPDATE и DELETE должен обновлять индекс. На write-heavy таблицах это быстро накапливается.
Поэтому не индексируйте наугад. Индексируйте столбцы, которые реально используются в запросах, и удаляйте неиспользуемые индексы. Неиспользуемый индекс — чистый убыток: замедляет запись и не даёт ничего взамен.
Рассмотрим запрос:
Order.where(user_id: id, status: 'paid')Добавим составной индекс на [:user_id, :status]:
add_index :orders, [:user_id, :status]Порядок столбцов решает всё. user_id идёт первым, потому что это селективный и всегда присутствующий фильтр равенства. B-tree сужает поиск до заказов одного пользователя, затем status фильтрует в пределах этого набора.
Бонус: так как user_id — левый столбец, этот же индекс покрывает запросы с фильтром только по user_id, не нужен отдельный индекс.
Обратный порядок [:status, :user_id] был бы хуже. У status всего несколько значений, поэтому он почти не сужает поиск перед тем, как дойти до user_id.
Прямо сейчас откройте свою базу данных и найдите таблицы с внешними ключами без индексов. Используйте EXPLAIN ANALYZE на частых запросах и добавьте недостающие индексы. Затем проверьте, не осталось ли неиспользуемых индексов — удалите их. Это сэкономит ресурсы и ускорит запись.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →