mongodb – count()比find()慢得多?

我正在使用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()的post,首先在游标中:

 "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 } } } } 

查找 – 在集合中查找文档并将光标返回到所选文档。 游标是指向查询结果集的指针。 客户端可以迭代游标来检索结果。

Count – count()相当于db.collection.find(query).count()

Count实际上是一个游标方法,shell只是提供一个快捷方式。 在Shell中,我们有count的快捷方式,如下所示db.collection.count(query)

原因:在查找操作中,我们没有迭代结果集,而在计数操作中,我们正在对结果集进行操作。 那会造成时间延迟。

https://docs.mongodb.com/manual/reference/method/db.collection.find/ https://docs.mongodb.com/manual/reference/method/db.collection.count/

你的答案的关键是

 //count "totalDocsExamined" : 1643722, 

VS

 //find "totalDocsExamined" : 0, 

查询查询完全在索引上运行,不会读取任何单个文档,而计数查询实际上是从数据库中读取每个文档。

原因是,您的find查询使用lean()选项。 根据mongoosedoc :

启用精简选项的查询返回的文档是纯javascript对象,而不是MongooseDocuments。 他们没有保存方法,getters / setters或其他mongoose魔术应用。

最重要的是,在您的精简查询查询中,您只需select() ing idstatus ,这似乎是投影( "transformBy" ... ),因此整个查询成为一个覆盖的查询,并且不必读取文档服务请求。