Как в MongoDB происходит индексация?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Индексация в 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 - максимальная производительность
- Регулярно мониторь - удаляй неиспользуемые индексы
Внимание к индексам — вот что отличает медленное приложение от быстрого.