Индексы и EXPLAIN

Ускоряем запросы индексами и учимся читать план выполнения через EXPLAIN.

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

Зачем нужны индексы

Без индекса поиск по условию — это полный перебор таблицы (full scan): база читает каждую строку. На миллионе строк это медленно. Индекс — как алфавитный указатель в книге: вместо листания всех страниц вы открываете нужную сразу. Технически это обычно B-дерево, отсортированное по значениям столбца.

Создание индекса

-- индекс по столбцу email (ускорит поиск по email)
CREATE INDEX idx_users_email ON users(email);

-- составной индекс по двум столбцам
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);

-- посмотреть индексы таблицы
SHOW INDEX FROM users;

Первичный ключ и UNIQUE-столбцы индексируются автоматически. Под внешние ключи в InnoDB индекс тоже создаётся.

EXPLAIN — что делает запрос

EXPLAIN показывает план: как MySQL собирается выполнять запрос, использует ли индекс и сколько строк просмотрит. Ключевой признак проблемы — type = ALL (полный перебор).

EXPLAIN SELECT * FROM users WHERE email = '[email protected]';
id | type  | key             | rows | Extra
 1 | ref   | idx_users_email |    1 | Using index condition

Здесь key = idx_users_email — индекс используется, rows = 1 — база найдёт строку сразу. Без индекса было бы type = ALL и rows во всю таблицу.

Цена индексов

Индексы не бесплатны. У них две стоимости:

  • Место на диске — индекс хранится отдельно.
  • Замедление записи — при каждом INSERT/UPDATE/DELETE индексы нужно обновлять.

Поэтому индексируют не всё подряд, а столбцы, по которым реально часто ищут, фильтруют, сортируют и соединяют (в WHERE, JOIN, ORDER BY). Лишние индексы только вредят.

Демонстрация на песочнице

Создание индекса — переносимо. Запрос отработает так же, но станет быстрее на больших объёмах:

CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  email TEXT,
  name TEXT
);

INSERT INTO users (email, name) VALUES
  ('[email protected]', 'Анна'),
  ('[email protected]', 'Борис'),
  ('[email protected]', 'Вера');

CREATE INDEX idx_users_email ON users(email);

SELECT name FROM users WHERE email = '[email protected]';

Вывод:

name
Борис

Итог

  • Индекс ускоряет поиск, избавляя от полного перебора таблицы.
  • Индексируйте столбцы из WHERE, JOIN, ORDER BY; первичный ключ индексируется сам.
  • EXPLAIN показывает план: type = ALL — тревожный знак полного перебора.
  • Индексы замедляют запись и занимают место — не плодите лишние.
Проверьте себя
1. Что произойдёт при поиске по столбцу без индекса на большой таблице?
Aзапрос вернёт ошибку
Bбаза выполнит полный перебор всех строк — медленно
Cпоиск будет невозможен
Dвернётся только первая строка
2. Какой признак в выводе EXPLAIN указывает на полный перебор таблицы?
Atype = ref
Btype = ALL
Ckey = PRIMARY
Drows = 1
3. Почему не стоит создавать индексы на всех столбцах подряд?
AMySQL не позволяет больше одного индекса
Bиндексы замедляют запись и занимают место на диске
Cиндексы ломают SELECT
Dэто не влияет ни на что
Поддержать проект