Использование индексов MongoDB

MongoDBBeginner
Практиковаться сейчас

Введение

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

Вы начнете с наблюдения за производительностью запроса без индекса. Затем вы создадите индексы по одному полю и составные индексы и увидите, как они значительно улучшают скорость запросов и сортировки. Наконец, вы научитесь управлять своими индексами, просматривая их список и удаляя. К концу этой лабораторной работы вы получите практическое понимание того, как создавать и использовать индексы для повышения эффективности ваших приложений MongoDB.

Запросы без индекса

Прежде чем создавать индекс, важно понять, как MongoDB работает без него. На этом шаге вы настроите пример коллекции, выполните запрос и проанализируете его план выполнения, чтобы увидеть влияние полного сканирования коллекции на производительность.

Сначала откройте MongoDB Shell (mongosh) для взаимодействия с вашей базой данных. Этот интерфейс командной строки позволяет выполнять команды непосредственно против вашего экземпляра MongoDB.

mongosh

Оказавшись в оболочке, вы увидите приглашение >. Давайте переключимся на новую базу данных с именем indexlab и вставим несколько примеров документов в коллекцию users. Если база данных или коллекция не существуют, MongoDB создаст их автоматически.

use indexlab
db.users.insertMany([
  { name: "Alice", age: 28, city: "New York" },
  { name: "Bob", age: 35, city: "San Francisco" },
  { name: "Charlie", age: 42, city: "Chicago" },
  { name: "David", age: 25, city: "New York" },
  { name: "Eve", age: 31, city: "San Francisco" }
]);

Теперь найдем всех пользователей старше 30 лет. Мы будем использовать метод .explain("executionStats"), чтобы увидеть, как MongoDB выполняет этот запрос. Этот метод предоставляет подробную статистику о плане выполнения запроса.

db.users.find({ age: { $gt: 30 } }).explain("executionStats");

Вывод предоставляет подробную статистику выполнения запроса. Обратите внимание на разделы winningPlan и executionStats.

Пример вывода (сокращенный):

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "COLLSCAN",
      "filter": { "age": { "$gt": 30 } },
      "direction": "forward"
    }
  },
  "executionStats": {
    "executionSuccess": true,
    "nReturned": 3,
    "executionTimeMillis": 0,
    "totalKeysExamined": 0,
    "totalDocsExamined": 5
  }
}

Ключевая информация здесь — stage: "COLLSCAN" и totalDocsExamined: 5.

  • COLLSCAN означает "Collection Scan" (сканирование коллекции). Это означает, что MongoDB пришлось проверить каждый документ в коллекции, чтобы найти те, которые соответствуют запросу.
  • totalDocsExamined: 5 подтверждает, что были просканированы все 5 документов в коллекции.

Хотя это быстро для небольшой коллекции, сканирование коллекции с миллионами документов будет очень медленным. На следующем шаге вы исправите это, добавив индекс.

Создание и использование индекса по одному полю

Теперь, когда вы увидели неэффективность сканирования коллекции, давайте повысим производительность, создав индекс. Индекс по полю age позволит MongoDB быстро находить соответствующие документы, не сканируя всю коллекцию.

Вы все еще должны находиться в оболочке mongosh из предыдущего шага.

Создайте индекс по полю age в порядке возрастания. 1 указывает на индекс по возрастанию, а -1 — на индекс по убыванию.

db.users.createIndex({ age: 1 });

MongoDB подтвердит успешное создание индекса. Имя по умолчанию для этого индекса будет age_1.

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

db.users.find({ age: { $gt: 30 } }).explain("executionStats");

Пример вывода (сокращенный):

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "FETCH",
      "inputStage": {
        "stage": "IXSCAN",
        "keyPattern": { "age": 1 },
        "indexName": "age_1"
      }
    }
  },
  "executionStats": {
    "executionSuccess": true,
    "nReturned": 3,
    "executionTimeMillis": 0,
    "totalKeysExamined": 3,
    "totalDocsExamined": 3
  }
}

Обратите внимание на существенные изменения в плане выполнения:

  • stage теперь IXSCAN, что означает "Index Scan" (сканирование индекса). Это указывает на то, что MongoDB использовала индекс age_1 для поиска соответствующих документов.
  • totalKeysExamined и totalDocsExamined теперь равны 3, а не 5. MongoDB пришлось просмотреть только 3 документа, соответствующих запросу через индекс, проигнорировав остальные 2. Это источник прироста производительности.

Использование составного индекса для сортировки

Индексы нужны не только для ускорения запросов; они также имеют решающее значение для эффективной сортировки. Когда вы сортируете по полю, которое не проиндексировано, MongoDB должна выполнять сортировку в памяти, что может быть медленным и потреблять значительный объем ОЗУ. Составной индекс, включающий несколько полей, может оптимизировать запросы, которые фильтруют и сортируют по этим полям.

Создадим составной индекс по полям city (по возрастанию) и age (по убыванию). Порядок полей в индексе важен для того, как он может быть использован.

db.users.createIndex({ city: 1, age: -1 });

Теперь выполним запрос, который сортирует пользователей сначала по городу, а затем по возрасту. Мы снова используем .explain(), чтобы подтвердить, что индекс используется для сортировки.

db.users.find().sort({ city: 1, age: -1 }).explain("executionStats");

Пример вывода (сокращенный):

{
  "queryPlanner": {
    "winningPlan": {
      "stage": "FETCH",
      "inputStage": {
        "stage": "IXSCAN",
        "keyPattern": { "city": 1, "age": -1 },
        "indexName": "city_1_age_-1"
      }
    }
  }
}

Этап IXSCAN показывает, что MongoDB использовала наш новый индекс city_1_age_-1. Поскольку данные уже упорядочены в индексе в соответствии с нашими критериями сортировки, MongoDB не нужно выполнять отдельный, затратный шаг сортировки в памяти.

Чтобы увидеть фактический отсортированный результат, выполните запрос без .explain().

db.users.find().sort({ city: 1, age: -1 });

Вывод:

[
  { _id: ObjectId("..."), name: 'Charlie', age: 42, city: 'Chicago' },
  { _id: ObjectId("..."), name: 'Alice', age: 28, city: 'New York' },
  { _id: ObjectId("..."), name: 'David', age: 25, city: 'New York' },
  { _id: ObjectId("..."), name: 'Bob', age: 35, city: 'San Francisco' },
  { _id: ObjectId("..."), name: 'Eve', age: 31, city: 'San Francisco' }
]

Документы правильно отсортированы сначала по city в алфавитном порядке, а затем по age от старшего к младшему в каждом городе, что соответствует определению составного индекса.

Управление и удаление индексов

Хотя индексы улучшают производительность чтения, они не бесплатны. Они потребляют дисковое пространство и добавляют небольшую нагрузку на операции записи (вставки, обновления и удаления). Поэтому рекомендуется периодически просматривать и удалять индексы, которые больше не используются.

Сначала вы можете перечислить все индексы в коллекции, используя метод getIndexes().

db.users.getIndexes();

Вывод:

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "age": 1 }, "name": "age_1" },
  { "v": 2, "key": { "city": 1, "age": -1 }, "name": "city_1_age_-1" }
]

Здесь показан индекс по умолчанию для поля _id, который автоматически создается для каждой коллекции, и два индекса, которые мы создали.

Предположим, мы определили, что составной индекс city_1_age_-1 больше не нужен. Вы можете удалить его с помощью метода dropIndex(), передав имя индекса в качестве аргумента.

db.users.dropIndex("city_1_age_-1");

MongoDB вернет объект, указывающий, сколько индексов существовало до операции удаления.

{ "nIndexesWas": 3, "ok": 1 }

Теперь проверьте, что индекс был удален, снова перечислив индексы.

db.users.getIndexes();

Вывод:

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "age": 1 }, "name": "age_1" }
]

Как видите, индекс city_1_age_-1 отсутствует. Правильное управление индексами является ключевой частью поддержания работоспособности и производительности базы данных.

Чтобы выйти из оболочки MongoDB, вы можете набрать exit или нажать Ctrl+D.

exit;

Резюме

В этой лабораторной работе вы изучили основные методы использования индексов MongoDB. Вы начали с наблюдения за COLLSCAN в запросе без индекса и поняли его ограничения по производительности. Затем вы создали индекс по одному полю, что изменило план запроса на гораздо более эффективный IXSCAN.

Кроме того, вы изучили составные индексы и увидели, как их можно использовать для оптимизации операций сортировки, избегая дорогостоящих сортировок в памяти. Наконец, вы узнали, как управлять индексами, перечисляя их с помощью getIndexes() и удаляя неиспользуемые с помощью dropIndex(). Эти навыки являются основополагающими для создания быстрых и масштабируемых приложений с использованием MongoDB.