MongoDB聚合看起来很慢

时间:2015-09-29 10:43:06

标签: mongodb

我有一个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
}

由于

2 个答案:

答案 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
}