Статьи

Кардинальные $ ins: производительность запросов MongoDB в диапазонах

Приветствую искателей приключений! Если вы путешествовали по территории индексации MongoDB в течение какого-то промежутка времени, возможно, вы слышали или получили следующую максиму:  если ваши запросы содержат предложение sort / orderby, добавьте отсортированное поле в конец индекса, обслуживающего запрос.

Во многих случаях при запросе документов, содержащих эквивалентные значения, вышеприведенная мантра очень полезна (под эквивалентностью я имею в виду запрос определенного значения в поле, например {«name»: «Charlie»}). Но как насчет следующего:

запрос

db.drivers.find({"country": {"$in": ["A", "G"]}).sort({"carsOwned": 1})

Показатель

{"country": 1, "carsOwned": 1}

Это спаривание не так эффективно, как можно было бы ожидать, хотя индекс следует максиму. Это потому, что есть очень специфическая ловушка, в которую может привести вас эта традиционная мудрость.

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

  • «Ранний индекс»
     Индексы заслуживают первоочередного внимания в процессе проектирования. Эффективность на уровне доступа к данным исторически отводилась роли, подобной DBA. который подсказывает тип слоев оптимизации после проектирования, которые стек документов-ориентированных баз данных все еще имеет возможность избежать.
  • «Индекс часто»
    Индексированные запросы работают лучше на несколько порядков, даже для небольших данных. Хотя неиндексированный запрос может занять 10 секунд, этот же запрос может занять всего 0 миллисекунд при правильном индексе.

  • Запросы «Полностью индексировать» используют индексы слева направо. Индекс может использоваться только в той степени, в которой запрос использует все поля в индексе и не пропускает ни одного.
  • «
    Сортировка по индексу» Если ваши запросы будут содержать предложение sort или orderby, добавьте отсортированное поле в ваш индекс.
  • команды
    • .explain () показывает, какой индекс (если есть) используется для данного запроса,
    • .ensureIndex () создает индексы,
    • .getIndexes () или .getIndexKeys () сообщают вам, какие у вас есть индексы.

Теперь вернемся к вопросу. Учитывая основы индексации, общепринятая мудрость говорит, что для следующего запроса:

db.collection.find({"country": "A"}).sort({"carsOwned": 1})

Мы должны создать следующий индекс:

db.collection.ensureIndex({"country": 1, "carsOwned": 1})

Что если большинство запросов к этим полям являются проверками «диапазона» вместо проверок «эквивалентности»? Как в:

db.collection.find({"country": {"$in": ["A", "G"]}}).sort({"carsOwned": 1})

Здесь мы используем $, но это относится ко всем операторам диапазона: $ gt, $ lt и т. Д.

Если вы видите, что подобные запросы работают плохо, и вы помните свои основы, вы запустите .explain () и увидите, что индекс используется. Но вы также увидите {scanAndOrder: true}, показывающий, что MongoDB выполнил операцию упорядочения. Вот где стоимость. scanAndOrder стоит дорого, потому что он сортирует документы в памяти. Этого следует избегать для больших наборов результатов, потому что он медленнее и требует больше ресурсов процессора.

Но забудьте о том, почему scanAndOrder работает медленно; зачем заказывать MongoDB, если мы уже учли заказ в нашем индексе? Легко: у нас нет.

Зачем? Причина проста и связана со структурой индекса, который мы создали. Для приведенного выше примера документы, имеющие {«страна»: «A»}, и документы, имеющие {«страна»: «G»}, отсортированы в индексе по {«carsOwned»: 1}, но они отсортированы независимо друг от друга. Они не отсортированы вместе! Рассмотрим схему ниже:

В левой части диаграммы ниже показан порядок, в котором MongoDB просматривает документы при сканировании созданного нами индекса. После того, как документы, соответствующие всем критериям, найдены, результаты должны быть заказаны. Правая часть показывает альтернативный индекс: {«carsOwned»: 1, «страна»: 1}. Смещая рассмотрение поля сортировки вперед (влево) в индексе, мы создаем сценарий, в котором MongoDB посещает документы в том порядке, в котором мы их запрашиваем. Этот тонкий момент эффективности привел к следующему практическому правилу при индексации:

Порядок полей в индексе должен быть:

  1. Сначала поля, по которым вы будете запрашивать точные значения.
  2. Во-вторых, поля, по которым вы будете сортировать.
  3. Наконец, поля, по которым вы будете запрашивать диапазон значений.

Есть ли компромисс? Да. Запрос будет посещать больше узлов индекса, чем это технически необходимо, поскольку прохождение части индекса для индекса будет происходить до сокращения по критериям диапазона («страна», в примере »). Итак, хотя мы рассматривали это новое практическое правило как чистую выгоду для многих запросов, имейте в виду, что количество ваших данных может дать разные результаты.

Я надеюсь, что это руководство полезно для вас. Удачи там, авантюристы!

С уважением,
Эрик @ MongoLab