← Назад к вопросам

Как в MongoDB происходит индексация?

2.0 Middle🔥 121 комментариев
#Базы данных (NoSQL)

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Индексация в MongoDB: Структуры и механизмы

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

Типы индексов в MongoDB

1. Single Field Index (индекс по одному полю)

Самый базовый и частый тип.

// Создание индекса по полю email
db.users.createIndex({ email: 1 })
// 1 = восходящий порядок (A→Z)
// -1 = нисходящий порядок (Z→A)

// Поиск будет быстрым
db.users.find({ email: "user@example.com" })

2. Compound Index (составной индекс)

Индекс по нескольким полям, важен порядок.

// Индекс по (userId, createdAt)
db.posts.createIndex({ userId: 1, createdAt: -1 })

// Этот запрос будет БЫСТРЫМ
db.posts.find({ userId: 123, createdAt: { $gt: ISODate("2024-01-01") } })

// Но этот будет медленным (нет условия на userId)
db.posts.find({ createdAt: { $gt: ISODate("2024-01-01") } })

3. Text Index (текстовый поиск)

Для полнотекстового поиска.

db.articles.createIndex({ title: "text", content: "text" })

// Быстрый поиск по словам
db.articles.find({ $text: { $search: "python mongodb" } })

4. Geospatial Index (географический индекс)

Для поиска по координатам.

db.places.createIndex({ location: "2dsphere" })

// Найти всё в радиусе 10км
db.places.find({
  location: {
    $near: {
      $geometry: { type: "Point", coordinates: [40.7128, -74.0060] },
      $maxDistance: 10000
    }
  }
})

5. TTL Index (временной индекс)

Автоматически удаляет документы по истечению времени.

db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })

// Сессия автоматически удалится через 1 час
db.sessions.insertOne({ userId: 123, createdAt: new Date() })

Как MongoDB использует B-tree

Структура B-tree индекса

MongoDB хранит индекс в виде B-tree — сбалансированного дерева поиска:

Индекс по полю "age":

           [25, 50, 75]
          /    |    |    \
    [10-24] [26-49] [51-74] [76-100]
     ↓        ↓        ↓       ↓
    doc1    doc5    doc10   doc20
    doc2    doc6    doc11   doc21
    doc3    doc7    doc15   doc25
    doc4    doc9

Это позволяет найти документ за O(log N) операций вместо O(N).

Процесс поиска с индексом

// Запрос
db.users.find({ age: { $gt: 30, $lt: 60 } })

// 1. MongoDB видит индекс по age
// 2. Идёт в B-tree, находит точку начала (30)
// 3. Сканирует только документы между 30 и 60 (range scan)
// 4. Возвращает результаты

// Без индекса:
// 1. MongoDB проверяет КАЖДЫЙ документ в коллекции (collection scan)
// 2. Медленно!

Выбор индексов в Python

При использовании PyMongo с Pymongo/Motor:

from pymongo import ASCENDING, DESCENDING

# Подключение
db = client[mydb]
collection = db[users]

# Создание индекса
collection.create_index([(email, ASCENDING)], unique=True)
collection.create_index([(userId, ASCENDING), (createdAt, DESCENDING)])

# Проверить существующие индексы
print(collection.list_indexes())

# Удалить индекс
collection.drop_index(email_1)

Индекс и сортировка

Важный момент: индекс помогает не только поиску, но и сортировке:

// Индекс: { createdAt: -1 }
db.posts.find().sort({ createdAt: -1 })
// Это будет ОЧЕНЬ быстро (уже отсортировано в индексе)

// Индекс: { userId: 1, createdAt: 1 }
db.posts.find({ userId: 5 }).sort({ createdAt: 1 })
// Быстро (индекс помогает обоим: поиску и сортировке)

db.posts.find({ userId: 5 }).sort({ createdAt: -1 })
// Медленнее (индекс помогает поиску, но не сортировке)

Covered Queries (закрытые запросы)

МонгоДБ может ответить НА ЗАПРОС ТОЛЬКО ИЗ ИНДЕКСА (без доступа к документам):

// Индекс: { userId: 1, email: 1 }
db.users.find(
  { userId: 123 },
  { _id: 0, userId: 1, email: 1 }  // Только поля из индекса!
)

// MongoDB вернёт результат ТОЛЬКО из индекса (очень быстро)
// Не нужно даже открывать документы в памяти

Объяснение плана запроса (explain)

from pymongo import ASCENDING

collection = db[users]
collection.create_index([(email, ASCENDING)])

# Посмотреть план выполнения
explain = collection.find({ email: user@example.com }).explain()

print(explain[executionStats][executionStages][stage])
# COLLSCAN = полный скан коллекции (плохо)
# IXSCAN = сканирование индекса (хорошо)
# FETCH = получение документов после индекса (нормально)

print(explain[executionStats][totalDocsExamined])  # Сколько проверено документов
print(explain[executionStats][totalKeysExamined])   # Сколько проверено ключей индекса
print(explain[executionStats][nReturned])           # Сколько вернулось результатов

# Идеально: totalKeysExamined == nReturned (covered query)

Стратегия индексирования: ESR правило

Для составных индексов используй ESR (Equality, Sort, Range):

// Запрос: find({ status: active, createdAt: {$gt: date} }).sort({ name: 1 })

// Правильный порядок индекса:
db.users.createIndex({
  status: 1,      // Equality (равенство)
  name: 1,        // Sort (сортировка)
  createdAt: 1    // Range (диапазон)
})

// Неправильный порядок (медленнее):
db.users.createIndex({
  createdAt: 1,
  status: 1,
  name: 1
})  // MongoDB будет менее эффективен

Проблемы и оптимизация

❌ Проблема: Слишком много индексов

// Это плохо:
db.users.createIndex({ email: 1 })
db.users.createIndex({ phone: 1 })
db.users.createIndex({ age: 1 })
db.users.createIndex({ country: 1 })
// ...

// Каждый индекс:
// - Занимает память
// - Замедляет вставку (нужно обновлять ВСЕ индексы)
// - Замедляет удаление

✅ Решение: Составные индексы

db.users.createIndex({ email: 1, phone: 1, age: 1 })
// Один индекс покрывает все три поля

❌ Проблема: Неправильный индекс не используется

db.users.createIndex({ email: 1 })
db.users.find({ name: John, email: john@example.com })
// MongoDB может использовать индекс только если поиск начинается с email

Мониторинг индексов

# Статистика использования индексов
db.command(aggregate, [system.profile], pipeline=[
  {: {
    _id: ,
    count: {: 1}
  }}
])

# Неиспользуемые индексы
db.command(aggregate, [system.profile], pipeline=[
  {: {executionStats.totalKeysExamined: 0}}
])

Резюме

  • B-tree индексы обеспечивают O(log N) поиск вместо O(N)
  • Составные индексы - используй ESR правило
  • Explain() - всегда проверяй план запроса
  • TTL индексы - для временных данных
  • Covered queries - максимальная производительность
  • Регулярно мониторь - удаляй неиспользуемые индексы

Внимание к индексам — вот что отличает медленное приложение от быстрого.