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

Original author: Eric
  • Translation
Если вы путешествовали по территории индексов MongoDB, вы возможно слышали принцип: Если ваши запросы содержат сортировку, то добавте сортированное поле в конец индекса который используется в этих запросах.

Во многих случаях когда запросы содержат условия равенства как например {“name”: “Charlie”}, принцип который выше очень полезен. Но что о нем можно сказать со следующим примером:

Запрос:
db.drivers.find({"country": {"$in": ["A", "G"]}).sort({"carsOwned": 1})
Индекс:
{"country": 1, "carsOwned": 1}

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

Давайте вспомним основы из документации MongoDB:
* «Индексы рано»
Индексы заслуживают рассмотрения в начале проектирования. Исторический, эффективность на уровне доступа к данным была переложена на администраторов баз данных, это создавало слой оптимизации после проектирования.
С документо-ориентированныим базами данных есть возможность этого избежать.
* «Индексы часто»
Индексированные запросы работают лучше на несколько порядков, даже на маленьких данных. В то время как без индекса запрос может занять 10 секунд, тот же запрос может занять 0 милисекунд с соответсвующим индексом.
* «Индексы полностью»
Запросы используют индексы слева направо. Индекс может быть использован только при условии что запрос использует все поля в индексе без пропусков.
* «Сортировка индекса»
Если ваш запрос будет содержать сортировку, то добавте сортированное поле в ваш индекс.
* «Команды»
.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})

Здесь мы использовали оператор $in, но кроме него есть ещё такие как: $gt, $lt, и др.
Если вы будете использовать подобный запрос, вы увидите что он не эффективен, при этом вы помните основы — нужно запустить .explain() и посмотреть какой индекс используется и как.
В результате выполнения .explain() Вы увидите {scanAndOrder: true}, что значит MongoDB выполняет сортировочные операции, а это дорогая операция т.к. MongoDB сортирует документы в памяти. Поэтому Вы должны избегать большых наборов данных т.к. это медленно и ресурсоемко.

Не нужно забывать, почему scanAndOrder медленный, почему MongoDB сортирует результат хотя у нас уже есть индекс с сортировкой? Ответ простой: у нас нет подходящего индекса.

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



На левой схеме показан порядок обхода документов по индексу который мы создали. После того как все документы будут найдены, их нужно будет отсортировать.
На правой схеме альтернативный индекс { “carsOwned”: 1, “country”: 1}. В этом случае найденые документы будут уже в отсортированном виде.
Этот тонкий момент эффективности привел к следующим правилам при индексации:

Порядок полей должен быть:
1. Сначала поля которые отбираются по точным значениям.
2. Далее поля по которым будет идти сортировка.
3. И в конце поля для диапазонного фильтра.

Концовка
Есть ли компроммис? Да. Запрос будет посещать несколько узлов индекса, что технический необходимо, т.к. обход сортированной части будет происходить до отфильтровывания.
Таким образом новое правило как чистая прибль для многих запросов, но не забывайте что сложность ваших данных может приводить к различным результатам.

Я надеюсь, что это руководство поможет Вам. Удачи.

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 15

    +14
    Чтобы писать хорошие статьи, надо хорошо знать язык.
    Чтобы делать хорошие переводы, надо хорошо знать два языка.
      +3
      Согласен. Но я не думаю что стану профессиональным писателем в ближайшее время.
      Хотел поделиться как технарь технарю, информацией которая показалась мне полезной.

      Считаете не стоит выкладывать переводы, со средним знанием языка?
        +1
        Считаю, что не нужно останавливается со словами «и так сойдет», когда очевидно можете сделать лучше невысокая ценой.
      +9
      “Таким образом новое правило как чистая прибль для многих запросов. ”. Неплохо.
        +5
        Таким образом, в то время как мы видели, это новое правило, как чистая прибыль для многих запросов, имейте в виду, что мощность данных может приводить к различным результатам.

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

        Google Translator detected. (проверил).
          –4
          Да, пользовался как словарем, для не знакомых фраз, без «копи-паста».
          +2
          Собственно статья не столько о MongoDB, сколько о B-Tree индексах. В реляционных БД ситуация будет аналогичная.
            +5
            Спасибо за перевод полезной статьи. Не обращайте внимания на снобов, им лишь бы повод придраться =) Кому информация полезна, и так поймёт.
              0
              Недавно опробовал MangoDB(написана разработчиком из DISQUS на python), так она рвет по производительности MongoDB и все остальные скоростные бд!
                +1
                Это что за хохма MangoDB?

                Прекрасный код:
                output = open('/dev/null', 'w')
                

                Мне кажется он может работать еще быстрее — и порвать абсолютно все базы в мире :)
                Чего уж там — в /dev/null то писать не напряжно :)
              +1
              А почему не использовать отдельные индексы по country и carsOwned? Какая в этому случае будет производительность?
                0
                Запрос может использовать только один индекс.
                0
                Такое ощущение, что перевод от Goole.translate ;)

                Я не переводчик, у меня нет ни одной переводной статьи, но есть небольшой опыт написания собственных…
                Не надо делать дословного перевода.
                В хорошем переводе, нужно понять суть фразы и пересказать её своими словами. Тогда он будет понятен и тебе и всем.

                  0
                  Я ничего не понял. Какая-то путаница. Подозреваю что автор что-то намудрил.

                  Only users with full accounts can post comments. Log in, please.