Как порядок составных индексов влияет на производительность MongoDB?

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

Представьте, что у нас есть коллекция всех людей на Земле с индексом sex (99,9% времени «мужской» или «женский», но тем не менее строковый (не двоичный)) и индексом name.

Если бы мы хотели иметь возможность выбирать всех людей из определенного sex с определенным name, например. все "мужчины" с именами "Джон", лучше иметь составной индекс с sex первым или name первым? Почему нет)?


person Redsandro    schedule 05.11.2015    source источник
comment
Я не думаю, что порядок имеет значение с точки зрения производительности, но с точки зрения повторного использования - когда вы создаете составное имя индекса, пол, индекс можно повторно использовать при запросе только имени (но не только для пола) - соответственно, когда вы создаете составной индекс секс, имя, индекс можно повторно использовать при запросе только пола (но не только имени).   -  person Smutje    schedule 05.11.2015
comment
Вам когда-нибудь придется их сортировать?   -  person Abdullah Rasheed    schedule 05.11.2015
comment
@inspired не эти ключи   -  person Redsandro    schedule 05.11.2015
comment
Это действительно зависит от вашего использования. У Монга есть много вариантов того, как вы работаете с индексами. Вы можете определить составные индексы или одиночные индексы; Mongo может пересекать одиночные индексы в других, чтобы выполнить ваш запрос. Существуют и другие концепции, такие как индекс для покрытия вашего запроса, которые имеют некоторые ограничения. Так что это действительно зависит от каждого конкретного запроса, который вы хотите сделать, и их ожидаемого формата документа. Можете ли вы дать более подробную информацию о вашем случае использования?   -  person cenouro    schedule 05.11.2015
comment
@MarkPieszak Этот вопрос не является повторением этого другого вопроса, также MongoDB каким-то образом объединяет составной ключ - не очень хороший ответ, и ответ вроде правильный (был бы для нормального составного формирования индексов), но также не   -  person Sammaye    schedule 05.11.2015
comment
@Smutje Порядок имеет большое значение, особенно при использовании определенных операторов, таких как $in, например: blog.mongolab.com/2012/06/cardinal-ins   -  person Sammaye    schedule 05.11.2015


Ответы (3)


Редсандро,

Вы должны учитывать Index Cardinality и Selectivity.


1. Мощность индекса

Кардинальность индекса относится к тому, сколько возможных значений может быть для поля. Поле sex имеет только два возможных значения. У него очень низкое количество элементов. Другие поля, такие как names, usernames, phone numbers, emails и т. д., будут иметь более уникальное значение для каждого документа в коллекции, что считается высоким количеством элементов.

  • Большая мощность

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

    Если у вас есть индекс sex и вы ищете мужчин по имени Джон. Вы бы сузили область результатов примерно на 50 %, если бы сначала проиндексировали sex. И наоборот, если вы индексируете по name, вы немедленно сузите набор результатов до минутной доли пользователей по имени Джон, а затем обратитесь к этим документам, чтобы проверить пол.

  • Практическое правило

    Попробуйте создать индексы по high-cardinality ключам или сначала поместите high-cardinality ключей в составной индекс. Подробнее об этом можно прочитать в разделе о составных индексах в книге:

    Полное руководство по MongoDB


2. Избирательность

Кроме того, вы хотите использовать индексы выборочно и написать запросы, которые ограничивают количество возможных документов с индексируемым полем. Для простоты рассмотрим следующую коллекцию. Если ваш индекс {name:1}, если вы запустите запрос { name: "John", sex: "male"}. Вам нужно будет отсканировать 1 документ. Потому что вы позволили MongoDB быть избирательной.

{_id:ObjectId(),name:"John",sex:"male"}
{_id:ObjectId(),name:"Rich",sex:"male"}
{_id:ObjectId(),name:"Mose",sex:"male"}
{_id:ObjectId(),name:"Sami",sex:"male"}
{_id:ObjectId(),name:"Cari",sex:"female"}
{_id:ObjectId(),name:"Mary",sex:"female"}

Рассмотрим следующую коллекцию. Если ваш индекс {sex:1}, если вы запустите запрос {sex: "male", name: "John"}. Вам нужно будет отсканировать 4 документов.

{_id:ObjectId(),name:"John",sex:"male"}
{_id:ObjectId(),name:"Rich",sex:"male"}
{_id:ObjectId(),name:"Mose",sex:"male"}
{_id:ObjectId(),name:"Sami",sex:"male"}
{_id:ObjectId(),name:"Cari",sex:"female"}
{_id:ObjectId(),name:"Mary",sex:"female"}

Представьте возможные различия на большом наборе данных.


Небольшое объяснение составных индексов

Легко сделать неправильное предположение о составных индексах. Согласно документам MongoDB по составным индексам.

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

введите описание изображения здесь

При создании составного индекса 1 индекс будет содержать несколько полей. Итак, если мы проиндексируем коллекцию по {"sex" : 1, "name" : 1}, индекс будет выглядеть примерно так:

["male","Rick"] -> 0x0c965148
["male","John"] -> 0x0c965149
["male","Sean"] -> 0x0cdf7859
["male","Bro"] ->> 0x0cdf7859
...
["female","Kate"] -> 0x0c965134
["female","Katy"] -> 0x0c965126
["female","Naji"] -> 0x0c965183
["female","Joan"] -> 0x0c965191
["female","Sara"] -> 0x0c965103

Если мы проиндексируем коллекцию по {"name" : 1, "sex" : 1}, индекс будет выглядеть примерно так:

["John","male"] -> 0x0c965148
["John","female"] -> 0x0c965149
["John","male"] -> 0x0cdf7859
["Rick","male"] -> 0x0cdf7859
...
["Kate","female"] -> 0x0c965134
["Katy","female"] -> 0x0c965126
["Naji","female"] -> 0x0c965183
["Joan","female"] -> 0x0c965191
["Sara","female"] -> 0x0c965103

Наличие {name:1} в качестве префикса значительно улучшит работу с составными индексами. . На эту тему можно прочесть гораздо больше, надеюсь, это внесет некоторую ясность.

person Abdullah Rasheed    schedule 05.11.2015
comment
Вы забыли упомянуть об избирательности для одного, что очень важно - person Sammaye; 05.11.2015
comment
Проголосовал на данный момент. Я понимаю теорию, и это имеет смысл. Однако это подразумевает, что коллекции сопоставляются с составными индексами по одному полю за раз. (В отличие от field_a == index_a && field_b == index_b, где порядок не имеет значения, что я предположил, потому что имеет смысл перебирать коллекцию только один раз.) Есть ли источник, подтверждающий это? - person Redsandro; 05.11.2015
comment
Составные индексы @Redsandro в основном представляют собой деревья, и MongoDB обходит дерево вниз, самый простой способ увидеть это — выполнить кардинальные $ins blog.mongolab.com/2012/06/cardinal-ins большинство БД реализуют то или иное дерево, но такие технологии, как MySQL, как правило, содержат гораздо более крупные деревья, которые также позволяют перемещаться вверх и вниз и бок о бок и т. д. и т. д. - person Sammaye; 05.11.2015
comment
@Redsandro проверить, что я добавил к своему ответу. - person Abdullah Rasheed; 06.11.2015
comment
Герой. Следуя этому примеру и просто переупорядочив индексы в моем составном индексе, я получил огромную разницу в скорости запросов. Он перешел от десятисекундного запроса к 0,1-секундному запросу в базе данных с 2 миллионами документов. Спасибо! - person Mr.Bigglesworth; 28.08.2018
comment
@AbdullahRasheed Имеет ли индекс 2dsphere (каждый документ имеет уникальные данные о местоположении) высокую кардинальность и поэтому должен ли он быть первым в индексе/запросе? И как определить кардинальность поля $nin? - person Manuel; 11.07.2019
comment
@AbdullahRasheed Я должен был принять этот ответ давным-давно. - person Redsandro; 09.04.2020
comment
да, я думал, что порядок клавиш в JavaScript был непредсказуемым, но я думаю, что теперь он предсказуем: stefanjudis.com/today-i-learned/ - person Ben Lorantfy; 12.06.2020
comment
индексировать {страна: 1, язык: 1} покрыть запрос, например {страна: {$in: {нас, япония, английский}, язык: {$in: {английский, арабский} }}} - person Panic; 28.04.2021
comment
должно ли поле запроса $in быть префиксом составного индекса? - person Panic; 28.04.2021
comment
@AbdullahRasheed см. stackoverflow.com/a/67847321/4417769 - person sezanzeb; 05.06.2021

Я собираюсь сказать, что провел эксперимент и обнаружил, что, похоже, нет никакого снижения производительности за использование в первую очередь плохо различимого индексного ключа. (Я использую mongodb 3.4 с wiredtiger, который может отличаться от mmap). Я вставил 250 миллионов документов в новую коллекцию под названием items. Каждый документ выглядел так:

{
    field1:"bob",
    field2:i + "",
    field3:i + ""

"field1" всегда было равно "bob". "field2" было равно i, поэтому оно было совершенно уникальным. Сначала я провел поиск по field2, и сканирование 250 миллионов документов заняло больше минуты. Затем я создал такой индекс:

`db.items.createIndex({field1:1,field2:1})`

Конечно, field1 имеет значение «bob» для каждого отдельного документа, поэтому индекс должен искать несколько элементов, прежде чем найти нужный документ. Однако это был не тот результат, который я получил.

Я сделал еще один поиск в коллекции после завершения создания индекса. На этот раз я получил результаты, которые я перечислил ниже. Вы увидите, что "totalKeysExamined" каждый раз равно 1. Так что, возможно, с проводным тигром или чем-то еще они придумали, как сделать это лучше. Я читал, что wiredtiger фактически сжимает префиксы индексов, так что это может иметь какое-то отношение к этому.

db.items.find({field1:"bob",field2:"250888000"}).explain("executionStats")

{
    "executionSuccess" : true,
    "nReturned" : 1,
    "executionTimeMillis" : 4,
    "totalKeysExamined" : 1,
    "totalDocsExamined" : 1,
    "executionStages" : {
        "stage" : "FETCH",
        "nReturned" : 1,
        "executionTimeMillisEstimate" : 0,
        "works" : 2,
        "advanced" : 1,
        ...
        "docsExamined" : 1,
        "inputStage" : {
            "stage" : "IXSCAN",
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 0,
            ...
            "indexName" : "field1_1_field2_1",
            "isMultiKey" : false,
            ...
            "indexBounds" : {
                "field1" : [
                    "[\"bob\", \"bob\"]"
                ],
                "field2" : [
                    "[\"250888000\", \"250888000\"]"
                ]
            },
            "keysExamined" : 1,
            "seeks" : 1
        }
    }

Затем я создал индекс для field3 (который имеет то же значение, что и поле 2). Затем я искал:

db.items.find({field3:"250888000"});

Это заняло те же 4 мс, что и с составным индексом. Я повторил это несколько раз с разными значениями для field2 и field3 и каждый раз получал незначительные различия. Это говорит о том, что с помощью wiredtiger нет потери производительности из-за плохой дифференциации в первом поле индекса.

person user3413723    schedule 10.07.2017
comment
keysExamined здесь означает количество отдельных индексов, которые он просматривал, а не количество просматриваемых частей индекса. Я думаю, что любая разница между двумя индексными порядками будет невероятно мала по сравнению с общим временем выборки документа, поэтому, если мы хотим получить реальное представление о разнице в производительности, нам нужно запустить сценарий нагрузочного тестирования. довольно продолжительный период времени. - person willis; 09.05.2019
comment
Я не думаю, что ваш вариант использования является хорошим примером снижения производительности с низкой кардинальностью, поскольку, в конце концов, составной ключ имеет высокую кардинальность. Правда, чтобы получить элемент, движку нужно было прочитать один дополнительный узел дерева (bob), но вы бы этого не заметили; следующее чтение в любом случае ведет себя как индекс с высоким кардинальностью. Проблема возникает, когда вы хотите найти человека по имени Джон Маккензи, возраст 34 года среди 250 миллионов человек, но ваш индекс только для возраста. Там движок найдет 5 миллионов записей с возрастом = 34 и должен искать эту конкретную запись в этом списке. Здесь индекс бесполезен. - person Guillermo Prandi; 20.07.2019

Обратите внимание, что множественные предикаты равенства не обязательно должны быть упорядочены от наиболее избирательного к наименее избирательному. Это руководство было предоставлено в прошлом, однако оно ошибочно из-за природы индексов B-Tree и того, как на листовых страницах B-Tree будет хранить комбинации всех значений полей. Таким образом, существует точно такое же количество комбинаций независимо от порядка клавиш.

https://www.alexbevi.com/blog/2020/05/16/optimizing-mongodb-compound-indexes-the-equality-sort-range-esr-rule/

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

person sezanzeb    schedule 05.06.2021