Монго тройной составной индекс
Если у вас есть двойной составной индекс { a: 1, b: 1}, для меня имеет смысл, что индекс не будет использоваться, если вы выполняете запрос только по одному b (т.е. вы не можете "пропустить" 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 является многоключевым индексом с хорошей избирательностью.)
2 ответа
нижняя строка / tl; dr: Index 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 }});
Ваш пробег будет меняться.
Вы можете рассматривать запросы к A и C как особый случай запросов к A (в этом случае будет использоваться индекс). Использование индекса более эффективно, чем загрузка всего документа.
Предположим, что вы хотите получить все документы с A от 7 до 13 и C от 5 до 8.
Если бы у вас был индекс только для A: база данных могла бы использовать индекс для выбора документов с A между 7 и 13, но, чтобы убедиться, что C был между 5 и 8, ей также пришлось бы получать соответствующие документы.
Если у вас был индекс для A, B и C: база данных могла бы использовать индекс для выбора документов с A между 7 и 13. Поскольку значения C уже сохранены в записях индекса, она могла бы определить, соответствует ли документы также соответствуют критерию С, без необходимости извлечения этих документов. Таким образом, вы избежите чтения с диска, с лучшей производительностью.