我在 MongoDB 中有以下集合:
{
"_id" : ObjectId("..."),
"assetId" : "...",
"date" : ISODate("..."),
...
}
我需要做非常简单的事情 - 查找每个设备/ Assets 的最新记录。我有以下查询:
db.collection.aggregate([
{ "$match" : { "assetId" : { "$in" : [ up_to_80_ids ]} } },
{ "$group" :{ "_id" : "$assetId" , "date" : { "$last" : "$date"}}}
])
整个表大约 20Gb。当我尝试执行此查询时,大约需要 8 秒,这没有任何意义,因为我指定只应选择 $last 记录。 assetId 和 date 均已编入索引。如果我在组之前添加 { $sort : { date : 1 } } ,它不会改变任何内容。
基本上,我的查询结果不应取决于数据大小。我唯一需要的是每个设备/ Assets 的最高记录。如果我执行 80 个单独的查询,则需要几毫秒的时间。
有什么方法可以让 MongoDB 不遍历整个表吗?看起来数据库并没有减少而是处理一切?!好吧,我知道这种行为应该有一些充分的理由,但我在文档或论坛上找不到任何内容。
更新:
最终找到了 2.4.6 解释查询的正确语法:
db.runCommand( { aggregate: "collection", pipeline : [...] , explain : true })
结果:
{ "serverPipeline" : [ { "query" : { "assetId" : { "$in" : [ "52744d5722f8cb9b4f94d321", "52791fe322f8014b320dae41", "52740f5222f8cb9b4f94d306", ... must remove some because of SO limitations "52744d1722f8cb9b4f94d31d", "52744b1d22f8cb9b4f94d308", "52744ccd22f8cb9b4f94d319" ] } }, "projection" : { "assetId" : 1, "date" : 1, "_id" : 0 }, "cursor" : { "cursor" : "BtreeCursor assetId_1 multi", "isMultiKey" : false, "n" : 960881, "nscannedObjects" : 960881, "nscanned" : 960894, "nscannedObjectsAllPlans" : 960881, "nscannedAllPlans" : 960894, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 9, "nChunkSkips" : 0, "millis" : 6264, "indexBounds" : { "assetId" : [ [ "52740baa22f8cb9b4f94d2e8", "52740baa22f8cb9b4f94d2e8" ], [ "52740bed22f8cb9b4f94d2e9", "52740bed22f8cb9b4f94d2e9" ], [ "52740c3222f8cb9b4f94d2ea", "52740c3222f8cb9b4f94d2ea" ], .... [ "5297770a22f82f9bdafce322", "5297770a22f82f9bdafce322" ], [ "529df5f622f82f9bdafce429", "529df5f622f82f9bdafce429" ], [ "529f6a6722f89deaabbf9881", "529f6a6722f89deaabbf9881" ], [ "52a6e35122f89ce6e2cf4267", "52a6e35122f89ce6e2cf4267" ] ] }, "allPlans" : [ { "cursor" : "BtreeCursor assetId_1 multi", "n" : 960881, "nscannedObjects" : 960881, "nscanned" : 960894, "indexBounds" : { "assetId" : [ [ "52740baa22f8cb9b4f94d2e8", "52740baa22f8cb9b4f94d2e8" ], [ "52740bed22f8cb9b4f94d2e9", "52740bed22f8cb9b4f94d2e9" ], [ "52740c3222f8cb9b4f94d2ea", "52740c3222f8cb9b4f94d2ea" ], ....... [ "529df5f622f82f9bdafce429", "529df5f622f82f9bdafce429" ], [ "529f6a6722f89deaabbf9881", "529f6a6722f89deaabbf9881" ], [ "52a6e35122f89ce6e2cf4267", "52a6e35122f89ce6e2cf4267" ] ] } } ], "oldPlan" : { "cursor" : "BtreeCursor assetId_1 multi", "indexBounds" : { "assetId" : [ [ "52740baa22f8cb9b4f94d2e8", "52740baa22f8cb9b4f94d2e8" ], [ "52740bed22f8cb9b4f94d2e9", "52740bed22f8cb9b4f94d2e9" ], [ "52740c3222f8cb9b4f94d2ea", "52740c3222f8cb9b4f94d2ea" ], ........ [ "529df5f622f82f9bdafce429", "529df5f622f82f9bdafce429" ], [ "529f6a6722f89deaabbf9881", "529f6a6722f89deaabbf9881" ], [ "52a6e35122f89ce6e2cf4267", "52a6e35122f89ce6e2cf4267" ] ] } }, "server" : "351bcc56-1a25-61b7-a435-c14e06887015.local:27017" } }, { "$group" : { "_id" : "$assetId", "date" : { "$last" : "$date" } } } ], "ok" : 1 }
请您参考如下方法:
您的 explain
输出表明有 960,881 个项目与您的 $match
阶段中的 assetId 匹配。 MongoDB 使用 assetId
上的索引找到所有这些,并将它们全部流式传输到 $group
阶段。这很贵。目前MongoDB并没有对聚合管道进行太多的全管道优化,所以你所写的就是你所得到的,几乎是这样。
MongoDB 可以通过按 assetId 升序和日期降序排序来优化此管道,然后应用 SERVER-9507 中建议的优化。但这还没有实现。
目前,您最好的做法是对每个 assetId 执行此操作:
db.collection.find({assetId: THE_ID}).sort({date: -1}).limit(1)