【发布时间】:2014-01-15 08:55:45
【问题描述】:
我在 MongoDB 中有以下集合:
{
"_id" : ObjectId("..."),
"assetId" : "...",
"date" : ISODate("..."),
...
}
我需要做一件非常简单的事情 - 查找每个设备/资产的最新记录。我有以下查询:
db.collection.aggregate([
{ "$match" : { "assetId" : { "$in" : [ up_to_80_ids ]} } },
{ "$group" :{ "_id" : "$assetId" , "date" : { "$last" : "$date"}}}
])
整个表大约 20Gb。当我尝试执行此查询时,它大约需要 8 秒,这没有任何意义,因为我指定只应选择 $last 记录。 assetId 和 date 都被索引。如果我在 group 之前添加 { $sort : { date : 1 } } 它不会改变任何东西。
基本上,我的查询结果不应该取决于数据大小。我唯一需要的是每个设备/资产的最高记录。如果我改为执行 80 个单独的查询,则需要几毫秒。
有没有办法让 MongoDB 不遍历整个表?看起来数据库并没有减少而是处理所有内容?!好吧,我知道这种行为应该有充分的理由,但我在文档或论坛上找不到任何东西。
更新:
最终找到了 2.4.6 的解释查询的正确语法:
db.runCommand( { aggregate: "collection", pipeline : [...] , explain : true })
结果:
{ “服务器管道”:[ { “询问” : { “资产ID”:{ “$in”:[ "52744d5722f8cb9b4f94d321", "52791fe322f8014b320dae41", "52740f5222f8cb9b4f94d306", ...由于 SO 限制,必须删除一些 "52744d1722f8cb9b4f94d31d", "52744b1d22f8cb9b4f94d308", “52744ccd22f8cb9b4f94d319” ] } }, “投影”:{ “资产ID”:1, “日期”:1, “_id”:0 }, “光标”:{ "cursor" : "BtreeCursor assetId_1 multi", “isMultiKey”:假, “n”:960881, “nscannedObjects”:960881, “nscanned”:960894, “nscannedObjectsAllPlans”:960881, “nscannedAllPlans”:960894, “scanAndOrder”:假, “indexOnly”:假, “nYields”:9, “nChunkSkips”:0, “毫”:6264, “索引边界”:{ “资产 ID”:[ [ "52740baa22f8cb9b4f94d2e8", “52740baa22f8cb9b4f94d2e8” ], [ "52740bed22f8cb9b4f94d2e9", “52740bed22f8cb9b4f94d2e9” ], [ “52740c3222f8cb9b4f94d2ea”, “52740c3222f8cb9b4f94d2ea” ], …… [ "5297770a22f82f9bdafce322", “5297770a22f82f9bdafce322” ], [ "529df5f622f82f9bdafce429", “529df5f622f82f9bdafce429” ], [ "529f6a6722f89deaabbf9881", “529f6a6722f89deaabbf9881” ], [ "52a6e35122f89ce6e2cf4267", “52a6e35122f89ce6e2cf4267” ] ] }, “所有计划”:[ { "cursor" : "BtreeCursor assetId_1 multi", “n”:960881, “nscannedObjects”:960881, “nscanned”:960894, “索引边界”:{ “资产 ID”:[ [ "52740baa22f8cb9b4f94d2e8", “52740baa22f8cb9b4f94d2e8” ], [ "52740bed22f8cb9b4f94d2e9", “52740bed22f8cb9b4f94d2e9” ], [ "52740c3222f8cb9b4f94d2ea", “52740c3222f8cb9b4f94d2ea” ], ………… [ "529df5f622f82f9bdafce429", “529df5f622f82f9bdafce429” ], [ "529f6a6722f89deaabbf9881", “529f6a6722f89deaabbf9881” ], [ "52a6e35122f89ce6e2cf4267", “52a6e35122f89ce6e2cf4267” ] ] } } ], “旧计划”:{ "cursor" : "BtreeCursorassetId_1 multi", “索引边界”:{ “资产 ID”:[ [ "52740baa22f8cb9b4f94d2e8", “52740baa22f8cb9b4f94d2e8” ], [ "52740bed22f8cb9b4f94d2e9", “52740bed22f8cb9b4f94d2e9” ], [ “52740c3222f8cb9b4f94d2ea”, “52740c3222f8cb9b4f94d2ea” ], ........... [ "529df5f622f82f9bdafce429", “529df5f622f82f9bdafce429” ], [ "529f6a6722f89deaabbf9881", “529f6a6722f89deaabbf9881” ], [ "52a6e35122f89ce6e2cf4267", “52a6e35122f89ce6e2cf4267” ] ] } }, “服务器”:“351bcc56-1a25-61b7-a435-c14e06887015.local:27017” } }, { “$组”:{ "_id" : "$assetId", “日期” : { "$last" : "$date" } } } ], “好”:1 }【问题讨论】:
-
我认为这里发生的事情是 mongodb 在 deviceId 上使用了索引。每个查询只能使用一个索引,因此不能使用日期索引。你能发布解释的输出吗?
-
用 explain() 输出更新了问题。如果 MongoDB 真的不能在聚合查询中使用多个索引,那么它解释了性能问题。
-
这个集合的索引是什么?您提到“asset_id”和“date”都已编入索引 - 但它们需要在同一个索引中才能获得最大收益。除非查询包含
$or子句,否则 MongoDB 2.4 将仅对每个查询使用单个索引。此外,虽然您只要求$last日期值,但聚合查询仍必须迭代所有匹配值才能找到每个组的最后一个值。当前查询的结果不取决于数据大小,但查询的执行时间会。 -
我会尝试:在
{assetId:1, date:-1}上添加索引;在$match之后添加{$sort: { 'date': -1}}操作;每组使用$first匹配而不是$last。 -
非常感谢@Stennie,您的 cmets 对找出问题所在非常有帮助。我认为应该在汇总文档中提到这一点,大阅读通知!我刚刚在docs.mongodb.org/manual/core/aggregation-pipeline 中找到了类似的示例。实际上,我对该集合几乎没有不同的查询,并且每次将assetId 和 date 一起使用。所以这是更多的设计错误。我将创建复合索引并删除assetId 和日期索引。
标签: mongodb