我有一个mongodb实例运行以下统计信息:
{
"db" : "s",
"collections" : 4,
"objects" : 1.23932e+008,
"avgObjSize" : 239.9999891553412400,
"dataSize" : 29743673136.0000000000000000,
"storageSize" : 32916655936.0000000000000000,
"numExtents" : 39,
"indexes" : 3,
"indexSize" : 7737839984.0000000000000000,
"fileSize" : 45009076224.0000000000000000,
"nsSizeMB" : 16,
"dataFileVersion" : {
"major" : 4,
"minor" : 5
},
"extentFreeList" : {
"num" : 0,
"totalSize" : 0
},
"ok" : 1.0000000000000000
}
我正在尝试运行以下查询:
db.getCollection('tick_data').aggregate(
[
{$group: {_id: "$ccy",min:{$first: "$date_time"},max:{$last: "$date_time"}}}
]
)
我在集合中设置了以下索引:
{
"ccy" : 1,
"date_time" : 1
}
查询需要510秒才能运行,即使集合相当大(约1.2亿个文档),感觉它也非常慢。我有一个简单的方法可以让它更快吗?
每个文档都有结构:
{
"_id" : ObjectId("56095bd7b2fc3e36d8d6ed52"),
"bid_volume" : "6.00",
"date_time" : ISODate("2007-01-01T00:00:07.904Z"),
"ccy" : "USDNOK",
"bid" : 6.2271700000000001,
"ask_volume" : "6.00",
"ask" : 6.2357699999999996
}
解释结果:
{
"stages" : [
{
"$cursor" : {
"query" : {},
"fields" : {
"ccy" : 1,
"date_time" : 1,
"_id" : 0
},
"plan" : {
"cursor" : "BasicCursor",
"isMultiKey" : false,
"scanAndOrder" : false,
"allPlans" : [
{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"scanAndOrder" : false
}
]
}
}
},
{
"$group" : {
"_id" : "$ccy",
"min" : {
"$first" : "$date_time"
},
"max" : {
"$last" : "$date_time"
}
}
}
],
"ok" : 1.0000000000000000
}
由于
答案 0 :(得分:1)
正如@Blakes Seven所说,$ group不能使用索引。请参阅this topic。
因此,您的查询已经是最佳的。优化此用例的一种可能方法是预先计算并保留副集合中的数据。
您可以尝试这种数据结构:
{
"_id" : ObjectId("560a5139b56a71ea60890201"),
"ccy" : "USDNOK",
"date_time_first" : ISODate("2007-01-01T00:00:07.904Z"),
"date_time_last" : ISODate("2007-09-09T00:00:07.904Z")
}
查询可以在几毫秒而不是500秒内完成,您可以从索引中受益。
然后,当然,每次从主集合中添加,更新或删除文档时,都需要更新副集合。
根据您需要数据的严重程度,您还可以选择跳过这个"实时更新流程"并且每天只使用批次完全重新生成一次侧面集合,并记住您的数据可能不是"新鲜"。
您可以解决的另一个问题:您的服务器肯定需要更多RAM和中央处理器。你的工作集可能不适合RAM,尤其是这种聚合。
此外,您可以充分利用SSD,我会强烈建议使用3节点Replicaset而不是单个实例进行生产。
答案 1 :(得分:0)
最后我写了一个运行0.002秒的函数。
function() {
var results = {}
var ccys = db.tick_data.distinct("ccy");
ccys.forEach(function(ccy)
{
var max_results = []
var min_results = []
db.tick_data.find({"ccy":ccy},{"date_time":1,"_id":0}).sort({"date_time":1}).limit(1).forEach(function(v){min_results.push(v.date_time)})
db.tick_data.find({"ccy":ccy},{"date_time":1,"_id":0}).sort({"date_time":-1}).limit(1).forEach(function(v){max_results.push(v.date_time)})
var max = max_results[0]
var min = min_results[0]
results[ccy]={"max_date_time":max,"min_date_time":min}
}
)
return results
}