Когда сайт только запущен, в базе несколько сотен строк, и любой запрос выполняется мгновенно. Через год-два в таблицах заказов, товаров и клиентов накапливаются сотни тысяч строк, и запросы, которых раньше никто не замечал, начинают выполняться секундами. Чаще всего дело не в слабом сервере, а в отсутствии нужного индекса. Один правильно поставленный индекс может ускорить тяжёлый запрос в разы, а иногда и в сотни раз.
Что такое индекс и как он работает
Индекс похож на алфавитный указатель в конце книги. Чтобы найти нужную тему, вы не листаете всю книгу, а находите номер страницы в указателе и сразу открываете нужное место. Без индекса MySQL при поиске по столбцу проверяет каждую строку таблицы по очереди — это называется полным сканированием. Индекс же хранит значения столбца в виде упорядоченного дерева, и сервер добирается до нужных строк за несколько шагов.
В таблицах InnoDB первичный ключ автоматически является индексом, поэтому поиск по id всегда быстрый. Проблемы начинаются при поиске по другим столбцам: номеру телефона, статусу, категории, дате. Именно для них нужны дополнительные индексы.
Каким столбцам нужен индекс
Лучшие кандидаты — столбцы, которые часто используются в запросах: для фильтрации в условии WHERE, для связи таблиц через JOIN и для сортировки в ORDER BY. Правильный путь — не гадать, какие запросы медленные, а выяснить это по логу медленных запросов или с помощью инструментов анализа запросов в приложении.
При этом ставить индекс на каждый столбец тоже неверно. Столбец, принимающий всего два-три значения, например статус «активен» или «неактивен», сам по себе как индекс почти бесполезен, ведь он возвращает половину строк. Такой столбец полезнее как часть составного индекса.
Смотрим на проблему через EXPLAIN
Перед добавлением индекса посмотрите текущий план выполнения запроса. В примере ниже в таблице заказов службы доставки ищут заказы по телефону клиента:
EXPLAIN SELECT id, status, total
FROM orders
WHERE phone = '998901234567'
ORDER BY created_at DESC;
Если в столбце type стоит ALL, в key — NULL, а в rows число, близкое к количеству строк в таблице, сервер просматривает таблицу целиком. Using filesort в столбце Extra означает, что результаты дополнительно сортируются отдельным шагом.
Добавляем индекс
Индекс добавляется командой ALTER TABLE или CREATE INDEX. Когда фильтрация и сортировка используются вместе, лучший результат даёт составной индекс: сначала столбец с условием равенства, затем столбец сортировки. Посмотреть существующие индексы можно через SHOW INDEX:
SHOW INDEX FROM orders;
ALTER TABLE orders
ADD INDEX idx_phone_created (phone, created_at),
ALGORITHM=INPLACE, LOCK=NONE;
В современных версиях MySQL добавление вторичного индекса в таблицу InnoDB обычно выполняется без блокировки чтения и записи. Часть ALGORITHM=INPLACE, LOCK=NONE явно этого требует: если так выполнить нельзя, MySQL вернёт ошибку вместо того, чтобы заблокировать таблицу. Тем не менее построение индекса на очень большой таблице требует времени и дополнительного места на диске, поэтому безопаснее делать это в часы минимального трафика и после резервной копии.
В составном индексе важен порядок
Индекс из нескольких столбцов используется слева направо. Индекс (phone, created_at) помогает и при поиске только по phone, и при запросе по phone вместе с created_at. Но при поиске только по created_at он обычно не используется, потому что первый столбец не участвует в условии. Поэтому порядок столбцов нужно выбирать по реальным запросам.
Когда индекс не может быть использован
Бывает, что индекс есть, а запрос всё равно медленный. У этого есть несколько типичных причин, и знание их экономит много времени:
- Если к столбцу применена функция, например
WHERE YEAR(created_at) = 2026, индекс не работает. Условие нужно переписать как диапазон:created_at >= '2026-01-01' AND created_at < '2027-01-01'. - Поиск вида
LIKE '%текст%'с процентом в начале не может использовать обычный индекс, аLIKE 'текст%'— может. - Если тип значения не совпадает с типом столбца, например текстовый столбец сравнивается с числом, MySQL может быть вынужден преобразовывать и проверять каждую строку.
Цена индекса
Индекс не бесплатен. Каждый дополнительный индекс занимает место на диске и должен обновляться при вставке или изменении строк, поэтому операции записи немного замедляются. Для сайтов, где в основном читают, эта цена обычно ничтожна, но в таблицах логов с очень частой записью лишние индексы могут стать нагрузкой. Полезно время от времени пересматривать неиспользуемые и дублирующие друг друга индексы и удалять их.
Пример: операторы службы доставки
На сайте службы доставки из Андижана misol.uz была панель для операторов. Когда клиент звонил, оператор искал его заказы по номеру телефона, и каждый поиск занимал четыре-пять секунд. В таблице заказов было полтора миллиона строк, а на столбце phone индекса не было. После добавления составного индекса, показанного выше, число просматриваемых строк в EXPLAIN упало с миллионов до нескольких десятков, и поиск стал выдавать результат мгновенно.
Для этого не понадобилось менять настройки сервера или переходить на более мощный тариф. Даже на виртуальном хостинге индекс можно добавить в своей базе через phpMyAdmin — это действие в пределах прав пользователя. Если на хостинге sayt.uz вам сложно определить, какой запрос тормозит, можно обратиться в техподдержку через кабинет.