Тройной составной индекс Mongo

Если у вас есть двойной составной индекс { a : 1, b : 1}, мне кажется, что индекс не будет использоваться, если вы запрашиваете только b (т. е. вы не можете «пропустить» a в вашем запросе). Однако индекс будет использоваться, если вы запрашиваете только a.

Однако, учитывая тройной составной индекс { a : 1, b: 1, c: 1}, моя команда объяснения показывает, что индекс используется при запросе по a и c (т. е. вы можете "пропустить" b в своем запросе).

Как Mongo может использовать индекс abc в запросе ac и насколько эффективен индекс в этом случае?

Предыстория:

Мой вариант использования заключается в том, что иногда я хочу запросить a, b, c, а иногда я хочу запросить a, c. Теперь я должен создать только 1 индекс для a, b, c или я должен создать один для a, c и один для a, b, c?

(Не имеет смысла создавать индекс для a,c,b, потому что c — это многоключевой индекс с хорошей избирательностью.)


person Zaid Masud    schedule 15.06.2012    source источник


Ответы (2)


Итог / tl;dr: Индекс b можно "пропустить", если a и c запрашиваются на равенство или неравенство, но не, например, для сортировки по c.

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

Все это довольно сложно, потому что это зависит от избирательности ваших индексов и от того, запрашиваете ли вы равенство, неравенство и/или сортировку, поэтому explain() — ваш единственный друг, но вот некоторые вещи, которые я нашел:

Предостережение. Сейчас мы получаем смесь экспериментальных результатов, рассуждений и предположений. Возможно, я слишком преувеличиваю аналогию Кайла и могу даже ошибаться (и мне не повезло, потому что результаты моего теста слабо совпадают с моими рассуждениями).

Ясно, что можно использовать индекс А, который, в зависимости от селективности А, безусловно, очень полезен. «Пропустить» B может быть сложно, а может и нет. Пусть это будет похоже на пример кулинарной книги Кайла :

French
    Beef
        ...
    Chicken
        Coq au Vin
        Roasted Chicken
    Lamb
        ...
    ...

Если вы сейчас попросите меня найти какое-нибудь французское блюдо под названием "Шатобриан", я могу использовать индекс A и, поскольку я не знаю ингредиента, мне придется просмотреть все блюда в A. С другой стороны, я знаю, что список блюд в каждой категории отсортирован по индексу C, поэтому мне нужно будет искать только строки, начинающиеся, скажем, с «Ча» в каждом списке ингредиентов. Если есть 50 ингредиентов, мне потребуется 50 поисков вместо одного, но это намного лучше, чем сканировать каждое французское блюдо!

В моих экспериментах это число было намного меньше, чем количество различных значений в b: оно никогда не превышало 2. Однако я проверял это только с одной коллекцией, и, вероятно, это связано с избирательностью b- показатель.

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

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

По сути, есть только два класса запросов: те, где nscanned ‹= 2 * limit, и те, которые должны сканировать всю коллекцию (120 тысяч документов). Индекс {a, b, c}:

// fast (range query on c while skipping b)
> db.Test.find({"a" : 43, "c" : { $lte : 45454 }});
// slow (sorting)
> db.Test.find({"a" : 43, "c" : { $lte : 45454 }}).sort({ "c" : -1});
> db.Test.find({"a" : 43, "c" : { $lte : 45454 }}).sort({ "b" : -1}); 

// fast (can sort on c if b included in the query)
> db.Test.find({"a" : 43, "b" : 7887, "c" : { $lte : 45454 }}).sort({ "c" : -1});

// fast (older tutorials claim this is slow)
> db.Test.find({"a" : {$gte : 43}, "c" : { $lte : 45454 }});

Ваш пробег будет варьироваться.

person mnemosyn    schedule 19.06.2012
comment
Спасибо, это, вероятно, лучшая информация, которую мы можем получить, учитывая (хорошо отмеченное) отсутствие авторитетных ссылок. В моем конкретном случае B является столбцом с низкой селективностью, и я делаю запрос, начинающийся с C. Итак, я полагаю, основываясь на вашем ответе, что мой индекс ABC почти так же хорош, как индекс AC для запроса на AC? Я знаю, что пробег будет варьироваться, поэтому пока я могу просто использовать индексы AC и ABC... - person Zaid Masud; 19.06.2012
comment
Я предполагаю, что это будет почти так же хорошо, как AC-индекс, если вы не сортируете по C, но explain() может вам сказать наверняка. Следите за nscanned, оно не должно быть больше n. - person mnemosyn; 19.06.2012

Вы можете рассматривать запросы к A и C как частный случай запросов к A (в этом случае будет использоваться индекс). Использование индекса более эффективно, чем загрузка всего документа.

Предположим, вы хотите получить все документы с A между 7 и 13 и C между 5 и 8.

Если бы у вас был индекс только для A: база данных могла бы использовать индекс для выбора документов с A между 7 и 13, но, чтобы убедиться, что C находится между 5 и 8, она также должна была бы получить соответствующие документы.

Если бы у вас был индекс для A, B и C: база данных могла бы использовать индекс для выбора документов с A между 7 и 13. Поскольку значения C уже сохранены в записях индекса, она могла бы определить, соответствуют ли соответствующие документы также соответствуют критерию C, без необходимости извлечения этих документов. Таким образом, вы избегаете чтения с диска с лучшей производительностью.

person Eduardo    schedule 15.06.2012
comment
Другими словами, вы говорите, что когда вы запрашиваете AC, то индекс ABC не лучше, чем индекс только для A? - person Zaid Masud; 18.06.2012
comment
Это лучше, потому что он может отфильтровывать нежелательные значения в C из самого индекса, не читая документ. - person Eduardo; 18.06.2012
comment
-1 @ Эдуардо Я не думаю, что понимаю большую часть ваших рассуждений. Как он может использовать C-часть индекса? Довольно ошибочно думать, что целью индекса является не загрузка всего документа. Фактически, вы должны думать об этом с точки зрения количества документов, которые необходимо отсканировать (nscan). - person Zaid Masud; 18.06.2012
comment
Если бы ваш запрос относился к A и C, и у вас был бы индекс только для A, то база данных должна была бы прочитать все документы, в которых A соответствует запросу, чтобы иметь возможность определить, соответствует ли их значение C запросу. Поскольку ваш индекс, как правило, будет намного меньше, чем коллекция, использование только индекса (с A и C) для определения того, какие документы соответствуют запросу, приведет к меньшему количеству операций чтения с диска и, следовательно, к повышению производительности. - person Eduardo; 18.06.2012
comment
Извините, но это просто не то, как работают индексы монго. Пожалуйста, прочитайте kylebanker.com/blog/2010/09 /21/the-joy-of-mongodb-indexes. Поскольку C упорядочен в группах B и не сортируется сам по себе, в своем ответе вам нужно показать, как C можно использовать. - person Zaid Masud; 18.06.2012
comment
Не имеет значения, если часть C индекса не отсортирована сама по себе. Основной поиск выполняется по A. Если часть A совпадает, то необходимо проанализировать часть C. Для анализа C лучше иметь его в индексе, чем читать соответствующий документ. - person Eduardo; 18.06.2012