我正在使用mongoose来计算与特定查询匹配的文档数量。我对此查询的索引是:{createdAt: -1, status: -1, oId: -1}
Mongo版本为3.2,该集合中的文档数量约为175万。
如果我这样做:
model.find({
createdAt: {'$gte': threeMonths, '$lt': today},
status: {'$in': model.STATUS_SET}
}).select({_id: 0, status: 1}).count().then((c) => result[alias] = c)
需要2分钟以上。但如果我这样做:
model.find({
createdAt: {'$gte': threeMonths, '$lt': today},
status: {'$in': model.STATUS_SET}
}).select({_id: 0, status: 1}).lean().then((c) => result[alias] = c.length)
然后大约需要2.5秒。
我做错了什么?我能做些什么来帮助加快速度?
编辑:解释日志。
对于计数:
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 0,
"executionTimeMillis" : 82671,
"totalKeysExamined" : 1749689,
"totalDocsExamined" : 1643722,
"executionStages" : {
"stage" : "COUNT",
"nReturned" : 0,
"executionTimeMillisEstimate" : 80960,
"works" : 1750066,
"advanced" : 0,
"needTime" : 1749689,
"needFetch" : 376,
"saveState" : 14662,
"restoreState" : 14662,
"isEOF" : 1,
"invalidates" : 0,
"nCounted" : 1643722,
"nSkipped" : 0,
"inputStage" : {
"stage" : "FETCH",
"nReturned" : 1643722,
"executionTimeMillisEstimate" : 80890,
"works" : 1750065,
"advanced" : 1643722,
"needTime" : 105967,
"needFetch" : 376,
"saveState" : 14662,
"restoreState" : 14662,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 1643722,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1643722,
"executionTimeMillisEstimate" : 3800,
"works" : 1749689,
"advanced" : 1643722,
"needTime" : 105967,
"needFetch" : 0,
"saveState" : 14662,
"restoreState" : 14662,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"createdAt" : -1,
"status" : -1,
"oId" : -1
},
"indexName" : "moderatedContent",
"isMultiKey" : false,
"direction" : "forward",
"indexBounds" : {
"createdAt" : [
"(new Date(1467195213000), new Date(1459246413000)]"
],
"status" : [
"[\"UNDECIDED\", \"UNDECIDED\"]",
"[\"APPROVED\", \"APPROVED\"]"
],
"oId" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 1749689,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0
}
}
},
"allPlansExecution" : [ ]
}
寻找。
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1643722,
"executionTimeMillis" : 1216,
"totalKeysExamined" : 1749689,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "PROJECTION",
"nReturned" : 1643722,
"executionTimeMillisEstimate" : 1080,
"works" : 1749690,
"advanced" : 1643722,
"needTime" : 105967,
"needFetch" : 0,
"saveState" : 13669,
"restoreState" : 13669,
"isEOF" : 1,
"invalidates" : 0,
"transformBy" : {
"_id" : 0,
"status" : 1
},
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1643722,
"executionTimeMillisEstimate" : 920,
"works" : 1749690,
"advanced" : 1643722,
"needTime" : 105967,
"needFetch" : 0,
"saveState" : 13669,
"restoreState" : 13669,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"createdAt" : -1,
"status" : -1,
"oId" : -1
},
"indexName" : "moderatedContent",
"isMultiKey" : false,
"direction" : "forward",
"indexBounds" : {
"createdAt" : [
"(new Date(1467195213000), new Date(1459246413000)]"
],
"status" : [
"[\"UNDECIDED\", \"UNDECIDED\"]",
"[\"APPROVED\", \"APPROVED\"]"
],
"oId" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 1749689,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0
}
}
}
首先在光标中使用.explain()发布:
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 0,
"executionTimeMillis" : 89191,
"totalKeysExamined" : 1749689,
"totalDocsExamined" : 1643722,
"executionStages" : {
"stage" : "COUNT",
"nReturned" : 0,
"executionTimeMillisEstimate" : 83400,
"works" : 1751709,
"advanced" : 0,
"needTime" : 1749689,
"needFetch" : 2019,
"saveState" : 15648,
"restoreState" : 15648,
"isEOF" : 1,
"invalidates" : 0,
"nCounted" : 1643722,
"nSkipped" : 0,
"inputStage" : {
"stage" : "FETCH",
"nReturned" : 1643722,
"executionTimeMillisEstimate" : 83260,
"works" : 1751708,
"advanced" : 1643722,
"needTime" : 105967,
"needFetch" : 2019,
"saveState" : 15648,
"restoreState" : 15648,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 1643722,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1643722,
"executionTimeMillisEstimate" : 8290,
"works" : 1749689,
"advanced" : 1643722,
"needTime" : 105967,
"needFetch" : 0,
"saveState" : 15648,
"restoreState" : 15648,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"createdAt" : -1,
"status" : -1,
"oId" : -1
},
"indexName" : "moderatedContent",
"isMultiKey" : false,
"direction" : "forward",
"indexBounds" : {
"createdAt" : [
"(new Date(1467195213000), new Date(1459246413000)]"
],
"status" : [
"[\"UNDECIDED\", \"UNDECIDED\"]",
"[\"APPROVED\", \"APPROVED\"]"
],
"oId" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 1749689,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0
}
}
}
}
答案 0 :(得分:4)
答案的关键是
//count
"totalDocsExamined" : 1643722,
VS
//find
"totalDocsExamined" : 0,
查询查询完全在索引上运行,并且在count查询实际读取db中的每个文档时不会读取任何单个文档。
原因是,您的查找查询使用lean()
选项。根据猫鼬doc:
启用精益选项的查询返回的文档是纯JavaScript对象,而不是MongooseDocuments。他们没有使用保存方法,getter / setter或其他Mongoose魔法。
最重要的是,在您的精简查找查询中,您只有select()
id
和status
,这似乎是预测("transformBy" ...
)所以整个查询变为 covered query ,并且不得读取任何文档来提供请求。
答案 1 :(得分:2)
查找 - 查找集合中的文档并将光标返回到所选文档。 Cursor是指向查询结果集的指针。客户端可以遍历游标以检索结果。
Count - count()等同于db.collection.find(query).count()
Count实际上是一个游标方法,shell只是提供了一个快捷方式。在Shell中,我们有计数快捷方式,如db.collection.count(query)
原因:在查找操作中,我们没有迭代结果集,而在count操作中,我们正在对结果集执行操作。那会导致时间延迟。
https://docs.mongodb.com/manual/reference/method/db.collection.find/ https://docs.mongodb.com/manual/reference/method/db.collection.count/