Как понять, какой индекс нужен MySQL-запросу

Индекс в MySQL нужен не “на всякий случай”, а под конкретный запрос. Если добавить индекс не на те поля или не в том порядке, он может почти не помочь. Если добавить слишком много индексов, запись в таблицу станет тяжелее, а поддержка базы сложнее.

Выбор индекса начинается с фактического SQL-запроса и EXPLAIN.

Начать с реального запроса

Нельзя проектировать индекс только по названию таблицы. Нужен запрос, который тормозит.

SELECT id, name, price
FROM products
WHERE supplier_id = 10
  AND status = 'A'
ORDER BY updated_at DESC
LIMIT 50;

Для такого запроса важны supplier_id, status и updated_at. Но порядок в индексе нужно выбирать с учётом фильтрации и сортировки.

EXPLAIN

EXPLAIN показывает, как MySQL планирует выполнить запрос.

EXPLAIN
SELECT id, name, price
FROM products
WHERE supplier_id = 10
  AND status = 'A'
ORDER BY updated_at DESC
LIMIT 50;

В первую очередь смотрят:

  • type;
  • possible_keys;
  • key;
  • rows;
  • Extra.

Если key пустой, индекс не используется. Если rows слишком большой, база просматривает много строк.

Составной индекс

Для примера выше может подойти индекс:

CREATE INDEX idx_products_supplier_status_updated
ON products (supplier_id, status, updated_at);

Он помогает отфильтровать поставщика и статус, а затем взять строки в нужном порядке. Но если запросы часто отличаются, индекс нужно проверять на реальных вариантах.

Leftmost prefix

MySQL использует составной индекс слева направо. Индекс на (supplier_id, status, updated_at) хорошо подходит для условий по supplier_id или supplier_id + status. Но не так полезен для запроса только по status.

-- индекс может использоваться нормально
WHERE supplier_id = 10 AND status = 'A'

-- индекс хуже подходит
WHERE status = 'A'

Поэтому порядок колонок в составном индексе важен.

WHERE и ORDER BY

Если запрос фильтрует и сортирует, хороший индекс может помочь и там, и там. Но это зависит от условий. Диапазонные условия могут ограничить дальнейшее использование индекса.

WHERE supplier_id = 10
  AND created_at >= '2026-06-01'
ORDER BY status

После диапазона по created_at сортировка по status может уже не использоваться так, как ожидается. Проверять нужно через EXPLAIN, а не предполагать.

JOIN

Для JOIN индексы нужны на полях соединения. Если orders.customer_id соединяется с customers.id, customer_id должен быть индексирован, особенно на большой таблице заказов.

SELECT o.id, c.name
FROM orders o
INNER JOIN customers c ON c.id = o.customer_id
WHERE o.status = 'new';
CREATE INDEX idx_orders_status_customer
ON orders (status, customer_id);

Если status хорошо фильтрует данные, такой индекс может быть полезен. Если почти все строки имеют status = new, пользы будет меньше.

Кардинальность

Индекс по полю с двумя значениями не всегда полезен. Например, status может иметь A/N, и если активных товаров 95%, индекс по одному status почти ничего не отфильтрует.

SHOW INDEX FROM products;

Кардинальность показывает примерную уникальность значений. Чем ниже избирательность, тем осторожнее нужно оценивать пользу индекса.

Лишние индексы

Каждый индекс ускоряет чтение определённых запросов, но замедляет INSERT, UPDATE и DELETE. Если таблица активно обновляется, лишние индексы могут вредить.

Перед добавлением нового индекса нужно проверить, нет ли уже похожего. Индекс (supplier_id, status, updated_at) частично покрывает запросы по supplier_id и supplier_id + status. Отдельный индекс только supplier_id может оказаться лишним, но это зависит от нагрузки.

Чек-лист

  1. Взять конкретный медленный SQL-запрос.
  2. Запустить EXPLAIN.
  3. Посмотреть key, rows и Extra.
  4. Определить поля WHERE, JOIN и ORDER BY.
  5. Продумать порядок колонок в составном индексе.
  6. Проверить leftmost prefix.
  7. Оценить кардинальность полей.
  8. Проверить, нет ли уже похожего индекса.
  9. Сравнить EXPLAIN до и после.

Индекс должен решать конкретную проблему конкретного запроса. Если после добавления индекса EXPLAIN не изменился или rows почти не уменьшился, индекс, скорее всего, выбран неправильно.

Комментарии (0)

Пока нет комментариев. Будьте первым!