我发现带有两个参数的计数查询花费的时间比生产数据库中预期的要长。我添加了一个索引(花了几个小时,该集合有超过1亿个文档),同时包含两个字段,这将我的.explain()调用的结果从IXSCAN改进为COUNT_SCAN。
现在看一下日志,我发现仍然有大量的IXSCAN planSummaries用于此计数查询:
2019-07-17T13:02:34.561 + 0000 I COMMAND [conn25293]命令 DatabaseName.CollectionName命令:count {count:“ CollectionName”, 查询:{userId:“ 5a4f82d4e4b09d5e0cdbae15”,状态:“完成”}} planSummary:IXSCAN {userId:1} keysExamined:299 docsExamined:299 numYields:7 reslen:44 locks:{全局:{acquisitionCount:{r:16}}, 数据库:{acquisitionCount:{r:8}},集合:{acquisitionCount:{r: 8}}}协议:op_query 124ms
在userId字段上有一个索引,但是我不明白为什么这个计数查询没有达到我的新索引。这是解释结果:
db.CollectionName.explain().count({ userId: "59adb07de4b00782f7620c11", status: "FINISHED" })
/* 1 */
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "DatabaseName.CollectionName",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"status" : {
"$eq" : "FINISHED"
}
},
{
"userId" : {
"$eq" : "59adb07de4b00782f7620c11"
}
}
]
},
"winningPlan" : {
"stage" : "COUNT",
"inputStage" : {
"stage" : "COUNT_SCAN",
"keyPattern" : {
"userId" : 1.0,
"status" : 1.0
},
"indexName" : "idx_userId_status",
"isMultiKey" : false,
"multiKeyPaths" : {
"userId" : [],
"status" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"indexBounds" : {
"startKey" : {
"userId" : "59adb07de4b00782f7620c11",
"status" : "FINISHED"
},
"startKeyInclusive" : true,
"endKey" : {
"userId" : "59adb07de4b00782f7620c11",
"status" : "FINISHED"
},
"endKeyInclusive" : true
}
}
},
"rejectedPlans" : []
},
"serverInfo" : {
"host" : "ip-10-114-1-8",
"port" : 27017,
"version" : "3.4.16",
"gitVersion" : "0d6a9242c11b99ddadcfb6e86a850b6ba487530a"
},
"ok" : 1.0
}
检查索引统计信息,我发现它已经使用了很多
{
"name" : "idx_userId_status",
"key" : {
"userId" : 1.0,
"status" : 1.0
},
"host" : "ip-address:27017",
"accesses" : {
"ops" : NumberLong(541337),
"since" : ISODate("2019-07-16T14:34:25.281Z")
}
}
我假设这意味着有时会使用它,但是由于某种原因而在其他时间没有使用它。为什么会这样呢?
答案 0 :(得分:1)
在我对MongoDB中的查询计划的理解中,数据库保留了某种查询计划的缓存,以便能够在无需过多思考的情况下选择正确的查询计划。
我的猜测是,在const collapseAll = (array) =>
array.filter(item => item.expandElements)
.forEach(item => (item.isExpanded = false, collapseAll(item.expandElements)));
的情况下,DB认为使用一个或多个不会带来太大的改变。
话虽如此,您仍然可以使用IXSCAN
(或更确切地说是explain(true)
)来尝试执行所有可能的计划。然后,如果您分析explain("allPlansExecution"
,可能会看到一些差异,可以解释查询计划的选择。
希望这会有所帮助:)