Индексы: зачем и какие бывают

Почему запрос по миллиону документов может быть мгновенным или мучительно медленным.

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

Проблема: полный перебор

Без индекса запрос db.users.find({ email: "[email protected]" }) заставляет MongoDB прочитать каждый документ коллекции и сравнить поле — это называется collection scan. На сотне документов незаметно, на миллионах — катастрофа. Индекс решает это, как алфавитный указатель в книге: вместо листания всех страниц вы идёте сразу к нужной.

Создаём индекс: createIndex

Чтобы ускорить поиск по email, строим индекс по этому полю (1 — по возрастанию):

db.users.createIndex({ email: 1 })

Теперь поиск по email идёт по индексу за логарифмическое время вместо полного перебора. Поле _id индексируется автоматически — поэтому поиск по нему и так быстрый.

Составной индекс: несколько полей

Если запросы часто фильтруют сразу по нескольким полям, строят составной (compound) индекс. Например, поиск заказов по городу и статусу:

db.orders.createIndex({ city: 1, status: 1 })

Важен порядок полей. Такой индекс ускорит запросы по city и по city + status, но не поможет запросу только по status — это «правило префикса»: индекс работает слева направо. Поэтому первым ставят поле, по которому фильтруют чаще.

Уникальный индекс

Индекс умеет ещё и гарантировать уникальность значения — как UNIQUE в SQL. Запретим повтор email:

db.users.createIndex({ email: 1 }, { unique: true })

Теперь вставка второго пользователя с тем же email вызовет ошибку дубликата. Это надёжная защита от дублей на уровне базы, а не только приложения.

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

Индексы не бесплатны. Каждый индекс занимает память и замедляет запись: при вставке и обновлении базе нужно поддерживать ещё и индексы. Поэтому не индексируют всё подряд — только поля, по которым реально и часто фильтруют или сортируют. Правило: индекс под запрос, а не «на всякий случай».

Посмотреть индексы коллекции и удалить ненужный:

db.users.getIndexes()
db.users.dropIndex({ email: 1 })

Итог

  • Без индекса запрос перебирает всю коллекцию (collection scan); индекс делает поиск быстрым.
  • Составной индекс работает по «правилу префикса» — порядок полей важен.
  • Уникальный индекс запрещает дубликаты; за индексы платят памятью и скоростью записи — индексируют под реальные запросы.
Проверьте себя
1. Что происходит при запросе по неиндексированному полю в большой коллекции?
AЗапрос отклоняется
BMongoDB перебирает все документы (collection scan) — медленно
CИндекс создаётся автоматически
DВозвращается пустой результат
2. Какому правилу подчиняется составной индекс { city: 1, status: 1 }?
AОн ускоряет любой запрос по любому из полей
BОн работает по префиксу слева направо: помогает запросам по city и city+status, но не только по status
CОн индексирует только status
DПорядок полей не важен
3. Зачем нужен уникальный индекс?
AЧтобы ускорить запись
BЧтобы запретить дубликаты значений поля на уровне базы
CЧтобы автоматически удалять старые документы
DЧтобы хранить массивы
Поддержать проект