Чтобы понять, что происходит под капотом в запросах mon go, вы можете использовать explain
. Например, рассмотрим следующий запрос: db.getCollection('users').find({"name":"ana"})
, который запрашивает неиндексированное поле. Вы можете использовать объяснение этого запроса, как показано ниже:
db.getCollection('users').find({"name":"ana"}).explain("executionStats")
Часть результата:
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "anonymous-chat.users",
"indexFilterSet" : false,
"parsedQuery" : {
"name" : {
"$eq" : "ana"
}
},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"name" : {
"$eq" : "ana"
}
},
"direction" : "forward"
},
"rejectedPlans" : []
},
Как видите, здесь у нас был COLLSCAN
- сканирование коллекции. Теперь мы просто запрашиваем _id и видим результат:
db.getCollection('users').find({"_id":ObjectId("5ee9b6c125b9a9a426d9965f")}).explain("executionStats")
Вот результат:
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "anonymous-chat.users",
"indexFilterSet" : false,
"parsedQuery" : {
"_id" : {
"$eq" : ObjectId("5ee9b6c125b9a9a426d9965f")
}
},
"winningPlan" : {
"stage" : "IDHACK"
},
"rejectedPlans" : []
},
Как мы видим, у нас есть IDHACK
при запросе только _id.
Теперь мы объединяем _id и name:
db.getCollection('users').find({"_id":ObjectId("5ee9b6c125b9a9a426d9965f"), "name":"ana"}).explain("executionStats")
Это результат:
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "anonymous-chat.users",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"_id" : {
"$eq" : ObjectId("5ee9b6c125b9a9a426d9965f")
}
},
{
"name" : {
"$eq" : "ana"
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"name" : {
"$eq" : "ana"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"multiKeyPaths" : {
"_id" : []
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"_id" : [
"[ObjectId('5ee9b6c125b9a9a426d9965f'), ObjectId('5ee9b6c125b9a9a426d9965f')]"
]
}
}
},
"rejectedPlans" : []
},
Как мы видим, индекс помог с производительностью запроса, так как у нас есть два этапы, этап IXSCAN
(сканирование индекса) и этап FETCH
, который фильтрует документы последнего этапа.
Теперь давайте запросим несколько неиндексированных полей, чтобы узнать о вашем втором вопросе:
db.getCollection('users').find({"name":"ana", "appId":1}).explain("executionStats")
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "anonymous-chat.users",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"appId" : {
"$eq" : 1.0
}
},
{
"name" : {
"$eq" : "ana"
}
}
]
},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"appId" : {
"$eq" : 1.0
}
},
{
"name" : {
"$eq" : "ana"
}
}
]
},
"direction" : "forward"
},
"rejectedPlans" : []
},
Есть только одно сканирование коллекции для нескольких полей, как мы видим выше.