Расскажите об индексах в MongoDB. Какие типы вы знаете и как оптимизируете запросы с их помощью?

Ответ

Да, работал. Индексы в MongoDB — это специальные структуры данных, которые хранят небольшую часть данных коллекции в упорядоченном виде. Их основная цель — кардинально ускорить операции поиска и сортировки. Однако они замедляют операции записи (вставка, обновление, удаление), так как при каждом изменении документа СУБД нужно обновлять и индексы.

Основные типы индексов:

  • Single Field: Индекс по одному полю. Самый простой и распространённый тип.
  • Compound: Составной индекс по нескольким полям. Порядок полей в индексе критически важен! Правило ESR (Equality, Sort, Range) помогает выбрать правильный порядок: сначала поля для точного совпадения, затем для сортировки, и в конце — для диапазонных запросов.
  • Multikey: Создаётся для полей, содержащих массивы. MongoDB создаёт отдельную запись в индексе для каждого элемента массива.
  • Text: Для реализации полнотекстового поиска по строковым полям.
  • Geospatial: Для запросов, связанных с геоданными (поиск по координатам, в радиусе и т.д.).
  • TTL (Time-To-Live): Специальный индекс, который позволяет MongoDB автоматически удалять документы по истечении определённого времени.
  • Unique: Гарантирует, что значения индексируемого поля (или комбинации полей) будут уникальными в коллекции.

Пример создания индекса в Go:

// Создание уникального составного индекса по полям 'username' (по возрастанию) и 'email' (по убыванию)
indexModel := mongo.IndexModel{
    Keys: bson.D{
        {Key: "username", Value: 1}, // 1 для ascending
        {Key: "email", Value: -1},    // -1 для descending
    },
    Options: options.Index().SetUnique(true),
}
_, err := collection.Indexes().CreateOne(context.Background(), indexModel)

Как я оптимизирую запросы:

  1. Анализ запросов: Использую метод explain("executionStats") для анализа плана выполнения запроса. Он показывает, используются ли индексы, сколько документов было просканировано (totalDocsExamined) и сколько возвращено (nReturned). В идеале эти два числа должны быть близки.
  2. Покрывающие запросы (Covered Queries): Стараюсь строить индексы так, чтобы они включали все поля, необходимые для запроса (и в find, и в projection). В этом случае MongoDB может вернуть результат прямо из индекса, не обращаясь к самим документам, что очень быстро.
  3. Правильный выбор полей для индекса: Индексирую поля, по которым часто происходит фильтрация (find), сортировка (sort) и агрегация ($lookup, $group).
  4. Контроль количества индексов: Избегаю создания избыточных индексов, так как каждый из них потребляет память и замедляет операции записи.

Ответ 18+ 🔞

А, индексы в MongoDB, ну это же классика, блядь! Это как если бы ты в своей библиотеке из овердохуища книг решил не рыться по всем полкам, а завел картотеку, где всё по полочкам разложено. Картотека — это индекс, сука. Без неё каждый запрос — это пиздец, ад и Израиль, надо весь шкаф перелопатить. А с ней — чик-чик, и готово.

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

Основные типы этих самых картотек-индексов:

  • Single Field (По одному полю): Самый простой, дедовский способ. Индексируешь, например, username. Ищешь Васю — быстро. Ищешь Петю — тоже быстро. Просто, как три копейки.
  • Compound (Составной, ёбаный): Вот тут уже начинается магия, а для многих — пиздец и боль. Индекс сразу по нескольким полям, например, city и salary. И запомни раз и нахуй: порядок полей — ВСЁ! Есть правило ESR (Equality, Sort, Range). Сначала поля, где ищешь точное совпадение (город), потом для сортировки (дата), и в самом конце — для диапазонов (зарплата от и до). Нарушишь порядок — получишь индекс, который нихуя не работает, а только место жрёт.
  • Multikey (Многоключевой): Для полей-массивов. Завёл у человека теги ["go", "beer", "mongodb"]. MongoDB возьмёт и для каждого элемента массива отдельную запись в индекс впендюрит. Удобно искать всех, кто любит пиво.
  • Text (Текстовый): Чтобы искать не только по точному совпадению, а по смыслу, словам. Для этого и нужен текстовый индекс, он умеет в стемминг, игнорирование стоп-слов и прочую хуйню.
  • Geospatial (Гео-индекс): Ищешь все бары в радиусе километра от своего дивана? Вот для этого инструмент. Индексируешь координаты и потом тыкаешь запросом «найди всё рядом».
  • TTL (Индекс с сроком годности): Гениальная штука, блядь! Создаёшь индекс с временем жизни. MongoDB сама, как ответственный дворник, будет подходить и удалять устаревшие документы. Автоматически, нахуй! Хранишь сессии или временные логи — идеально.
  • Unique (Уникальный): Железобетонная гарантия, что значения, например, в поле email, не повторятся. Попробуй вписать уже существующий email — получишь в ебало ошибку. Порядок наводит.

Вот тебе пример, как эту хуйню на Go создать:

// Делаем уникальный составной индекс: username по возрастанию, email по убыванию.
indexModel := mongo.IndexModel{
    Keys: bson.D{
        {Key: "username", Value: 1},  // 1 — это по возрастанию
        {Key: "email", Value: -1},    // -1 — это по убыванию, да, так можно
    },
    Options: options.Index().SetUnique(true), // Говорим: "Будь уникальным, сука!"
}
_, err := collection.Indexes().CreateOne(context.Background(), indexModel)

А теперь, как я обычно эту банду оптимизирую, чтобы всё летало:

  1. Вскрытие покажет: explain(). Это твой лучший друг и детектор пиздежа. Запускаешь запрос с explain("executionStats") и смотришь, блядь: сколько документов пришлось перерыть (totalDocsExamined), а сколько вернул (nReturned). Если перерыл миллион, а вернул десять — это пиздец, индекс либо кривой, либо его нет. Надо чинить.
  2. Покрывающие запросы — мечта идиота (в хорошем смысле). Суть в том, чтобы индекс содержал ВСЕ поля, которые нужны для ответа. Тогда MongoDB, хитрая жопа, даже не полезет в сами документы, а всё возьмёт прямо из индекса. Скорость — космос.
  3. Индексируй с умом, а не похуй. Часто фильтруешь по status и сортируешь по created_at? Значит, тебе скорее всего нужен составной индекс { status: 1, created_at: 1 }. Не индексируй всё подряд, каждый индекс — это как лишний рюкзак на спине при записи.
  4. Не умножай сущности без необходимости. Помни: каждый новый индекс — это тормоза на записи и пожиратель памяти. Создал индекс — прикинь, сколько пользы он принесёт против вреда. Иногда проще один сложный запрос подождать, чем всю систему на колени поставить ради него.

Вот так вот, если коротко и с матом. Главное — понимать, что индекс не волшебная таблетка, а инструмент. Им можно и дом построить, и себе по ебалу получить, если не умеешь.