Запрос выполняется быстрее без индекса
Ниже приведена упрощенная версия документа в моей базе данных:
{
_id : 1,
main_data : 100,
sub_docs: [
{
_id : a,
data : 22
},
{
_id: b,
data : 859
},
{
_id: c,
data: 151
},
... snip ...
{
_id: m,
data: 721
},
{
_id: n,
data: 111
}
]
}
Итак, представьте, что у меня есть миллион таких документов с различными значениями данных (скажем, 0 - 1000). В настоящее время мой запрос выглядит примерно так:
db.myDb.find(
{ sub_docs: { $elemMatch: { data: { $gte: 110, $lt: 160 } } } }
)
Также допустим, что приведенный выше запрос будет соответствовать только около 0,001% данных (таким образом, в общей сложности возвращается около 10 документов).
И у меня есть набор индексов с помощью:
db.myDb.ensureIndex( sub_docs.data )
Выполнение синхронизированного теста для этих данных, кажется, показывает, что это быстрее без какого-либо индекса, установленного на sub_docs.data.
Я использую Mongo 3.2.8.
Редактировать - Дополнительная информация:
Мой тест по времени - это Perl-скрипт, который запрашивает сервер, а затем извлекает соответствующие данные. Я запустил этот тест первым, когда у меня был включен индекс, однако медленное время запроса заставляло меня немного копать. Я хотел посмотреть, насколько плохим будет время запроса, если я уроню индекс, однако это улучшило время ответа на запрос! Я пошел немного дальше, я изобразил время ответа на запрос в сравнении с общим количеством документов в БД, оба графика показывают линейное увеличение времени запроса, но запрос с индексом увеличивается гораздо быстрее. Все время, пока я тестировал, я следил за использованием памяти сервера (которая невелика), поскольку моей первой мыслью было бы, что индекс не помещается в памяти.
Итак, в целом мой вопрос: почему для этого конкретного запроса этот запрос работает лучше без индекса и индекса? И есть ли способ улучшить скорость этого запроса с помощью лучшего индекса?
Обновить
Итак, прошло некоторое время, и я сузил его до индекса, не ограничивающего обе стороны параметров поиска запроса.
Приведенный выше запрос покажет границу индекса:
[-inf, 160]
Вместо 110-160. Я могу решить эту проблему с помощью функций index min и max следующим образом:
db.myDb.find(
{ sub_docs: { $elemMatch: { data: { $gte: 110, $lt: 160 } } } }
).min({'subdocs.data': 110}).max({'subdocs.data': 160})
Однако (если возможно) я бы предпочел другой способ сделать это, так как я хотел бы использовать агрегатную функцию (которая, кажется, не поддерживает функции индекса min/max)
1 ответ
Хорошо, так что мне удалось разобрать это в конце. По какой-то причине индекс не ограничивает запрос, как я ожидал.
Запуск этого:
db.myDb.find({ sub_docs: { $elemMatch: { data: { $gte: 110, $lt: 160 } } } }).explain()
Ниже приведен фрагмент того, что делает индекс:
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"sub_docs.data" : 1
},
"indexName" : "sub_docs.data_1",
"isMultiKey" : true,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"sub_docs.data" : [
"[-inf.0, 160.0)"
]
}
}
Вместо того, чтобы ограничить индекс между 110 и 160, он сканирует все документы, которые соответствуют ключу индекса, который меньше или равен 160. Я не включил его, но другим отклоненным планом было сканирование индекса от 110 до inf+. Вы можете решить эту проблему по минимальным / максимальным пределам, которые я упомянул выше в моем комментарии, однако это означает, что вы не можете использовать структуру агрегации, которая не работает.
Поэтому решение, которое я нашел, состояло в том, чтобы вытащить все данные, которые я хотел индексировать, в массив:
{
_id : 1,
main_data : 100,
index_values : [
22,
859,
151,
...snip...
721,
111
],
sub_docs: [
{
_id : a,
data : 22
},
{
_id: b,
data : 859
},
{
_id: c,
data: 151
},
... snip ...
{
_id: m,
data: 721
},
{
_id: n,
data: 111
}
]
}
И тогда я создаю индекс:
db.myDb.ensureIndex({index_values : 1})
А затем спросите об этом:
db.myDb.find({ index_values : { $elemMatch: { $gte: 110, $lt: 160 } } }).explain()
Который производит:
"indexBounds" : {
"index_values" : [
"[110.0, 160.0]"
]
}
Так что гораздо меньше документов, чтобы проверить сейчас!