Как влияет порядок составных индексов на производительность MongoDB?
Нам нужно создать составной индекс в том же порядке, в котором запрашиваются параметры. Этот заказ имеет значение для производительности вообще?
Представьте, что у нас есть коллекция всех людей на земле с индексом sex
(99,9% времени "мужской" или "женский", но, тем не менее, строка (не двоичная)) и индекс на name
,
Если бы мы хотели иметь возможность выбрать всех людей определенного sex
с определенным name
Например, все "мужчины" по имени "Джон", лучше иметь составной индекс с sex
первый или name
первый? Почему бы и нет)?
3 ответа
Redsandro,
Вы должны рассмотреть Index Cardinality
а также Selectivity
,
1. Индекс мощности
Индекс кардинальности относится к числу возможных значений для поля. Поле sex
имеет только два возможных значения. У него очень низкая мощность. Другие поля, такие как names, usernames, phone numbers, emails
и т. д. будет иметь более уникальное значение для каждого документа в коллекции, что считается большим количеством элементов.
Большая мощность
Чем больше мощность поля, тем полезнее будет индекс, потому что индексы сужают пространство поиска, делая его намного меньшим набором.
Если у вас есть индекс на
sex
и вы ищете мужчин по имени Джон. Вы бы сузили пространство результатов только примерно на 50%, если быsex
первый. И наоборот, если вы проиндексированыname
Вы бы сразу сузили набор результатов до минутной доли пользователей по имени Джон, а затем обратились бы к этим документам для проверки пола.Практическое правило
Попробуйте создать индексы на
high-cardinality
ключи или положитьhigh-cardinality
ключи сначала в составном индексе. Вы можете прочитать больше об этом в разделе по составным индексам в книге:
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}
в качестве префикса будет служить вам гораздо лучше при использовании составных индексов. По этой теме можно прочитать гораздо больше, я надеюсь, что это может дать некоторую ясность.
Я собираюсь сказать, что сам провел эксперимент с этим и обнаружил, что, похоже, нет потери производительности при использовании плохо различимого ключа индекса в первую очередь. (Я использую 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 не существует ухудшения производительности за плохую дифференциацию в первом поле индекса.
Обратите внимание, что несколько предикатов равенства не нужно упорядочивать от наиболее избирательного к наименее избирательному. Это руководство было предоставлено в прошлом, однако оно ошибочно из-за природы индексов B-Tree и того, как на листовых страницах B-Tree будет хранить комбинации всех значений полей. Таким образом, существует точно такое же количество комбинаций, независимо от порядка нажатия клавиш.
Эта статья в блоге не согласна с принятым ответом. Тест в другом ответе также показывает, что это не имеет значения. Автор этой статьи - «старший инженер по техническим услугам в MongoDB», что звучит для меня как уважаемый человек в этой теме, поэтому я полагаю, что порядок действительно не влияет на производительность в полях равенства. Вместо этого я буду следовать правилу ESR.