По умолчанию запросы возвращают все поля в соответствующих документах. Если вам нужны все поля, возврат полных документов будет более эффективен, чем когда сервер манипулирует результирующим набором с помощью критериев проекции.
Однако использование проекции для ограничения полей для возврата из результатов запроса может повысить производительность за счет:
- удаление ненужных полей из результатов запроса (экономия на пропускной способности сети)
- ограничение полей результатов для выполнения покрытого запроса (возврат индексированных результатов запроса без извлечения полных документов)
При использовании проекции для удаления неиспользуемых полей серверу MongoDB придется извлекать каждый полный документ в память (если его там еще нет) и фильтровать результаты для возврата. Такое использование проекции не уменьшает использование памяти или рабочего набора на сервере MongoDB, но может сэкономить значительную пропускную способность сети для результатов запроса в зависимости от модели данных и проецируемых полей.
Закрытый запрос - это особый случай, когда все запрошенные поля в результате запроса включены в используемый индекс, поэтому серверу не нужно извлекать полный документ. Покрываемые запросы могут повысить производительность (исключая выборку документов) и использование памяти (если другие запросы не требуют выборки того же документа).
Примеры
Для демонстрации с помощью mongo
оболочки представьте, что у вас есть документ, который выглядит следующим образом:
db.data.insert({
a: 'webscale',
b: new Array(10*1024*1024).join('z')
})
Поле b
может представлять выбор значений (или в этом случае очень длинную строку).
Затем создайте индекс, по {a:1}
которому часто используется поле, запрашиваемое вашим вариантом использования:
db.data.createIndex({a:1})
Простой findOne()
без критериев проекции возвращает результат запроса, который составляет около 10 МБ:
> bsonsize(db.data.findOne({}))
10485805
Добавление проекции {a:1}
ограничит вывод в поле a
и документ _id
(который включен по умолчанию). Сервер MongoDB по-прежнему манипулирует документом 10 МБ для выбора двух полей, но результат запроса теперь составляет всего 33 байта:
> bsonsize(db.data.findOne({}, {a:1}))
33
Этот запрос не покрыт, потому что полный документ должен быть выбран, чтобы обнаружить _id
значение. _id
Поле включено в результатах запроса по умолчанию , так как он является уникальным идентификатором для документа, но _id
не будет включен в вторичном индексе , если явно не добавлено.
totalDocsExamined
И totalKeysExamined
метрики в explain()
результатах будет показано , сколько документов и ключи индекса были рассмотрены:
> db.data.find(
{a:'webscale'},
{a:1}
).explain('executionStats').executionStats.totalDocsExamined
> 1
Этот запрос можно улучшить с помощью проекции, чтобы исключить _id
поле и выполнить покрытый запрос, используя только {a:1}
индекс. Покрываемому запросу больше не нужно извлекать документ размером ~ 10 МБ в память, поэтому он будет эффективен как по сети, так и по использованию памяти:
> db.data.find(
{a:'webscale'},
{a:1, _id:0}
).explain('executionStats').executionStats.totalDocsExamined
0
> bsonsize(db.data.findOne( {a:'webscale'},{a:1, _id:0}))
21
У меня медленные запросы MongoDB. Влияет ли возвращаемое подмножество на мой медленный запрос (у меня есть составной индекс на поле)?
Это не подлежит ответственности без контекста конкретного запроса, примера документа и полного вывода объяснения. Тем не менее, вы можете запустить некоторые тесты в своей среде для одного и того же запроса с прогнозом и без него, чтобы сравнить результат. Если ваш прогноз добавляет значительные накладные расходы к общему времени выполнения запроса (обработка и передача результатов), это может быть сильным намеком на то, что ваша модель данных может быть улучшена.
Если неясно, почему запрос медленный, было бы лучше опубликовать новый вопрос с конкретными деталями для расследования.