分析MongoDb聚合性能

时间:2014-12-02 05:36:36

标签: mongodb performance aggregation-framework

我试图从集合中获得聚合的毫秒(< second)响应时间。但是,即使数据量很小(约200MB),它目前也需要3-5秒。我对此集合的预期生产数据大约为每个碎片100GB。 我已经检查了以下内容 - 当我在每个分片上单独尝试查询时,响应时间相同。 - 检查了分析输出,我所能看到的只是高timeLockedMicros和numYield。 - 在MMS图表中也找不到任何异常。 我觉得这里有些愚蠢的东西。任何帮助进一步分析这一点非常感谢。我的群集和收集详细信息在下面提供

群集 - 34 GB的6个节点,4台核心机器(AWS m2.2xlarge) 数据大小 1,285 MB(每个碎片213 MB) 记录数= 550万(每个碎片约1 M)

记录样本

{
    "_id" : {
        "ItemID" : 105182,
        "DeviceType" : 16,
        "ItemVersionID" : 117971,
        "Timestamp" : ISODate("2014-11-14T00:00:00Z"),
        "RecordType" : 1
    },
    "Dim1ID" : 102260,
    "Dim2ID" : 313,
    "Dim3ID" : 1,
    "actionType" : {
        "1" : 66,
        "47" : 66,
        "42" : 72,
        "46" : 130
    }
}

查询

db.AggregateCollection.aggregate({ "$group" : { "_id" : { } , "type1" : { "$sum" : "$actionType.1"} , "type2" : { "$sum" : "$actionType.2"}}})

个人资料统计信息(来自一个分片)

"keyUpdates" : 0,
    "numYield" : 79,
    "lockStats" : {
        "timeLockedMicros" : {
            "r" : NumberLong(2981456),
            "w" : NumberLong(0)
        },
        "timeAcquiringMicros" : {
            "r" : NumberLong(3756),
            "w" : NumberLong(4)
        }
    },
    "responseLength" : 157,
    "millis" : 3268,
    "execStats" : {

    },

更新 谢谢你的快速回应。 Apreciate它。我喜欢你的新数据模型和索引。但是,我担心这不适合我目前的数据,因为, - 99%的记录将具有actionType.1和的soem值 - 99%的查询将选择actionType.1 因此,actiionType.K上的索引对我的猜测不会太大。

正如你在#2& #3,我们已经在使用Spark集群进行预聚合,它提升了MongoDb。

关于我的查询的更多信息 我之前分享的查询只是一个示例,仅用于对性能进行基准测试。我的实际查询将在一个或多个文件上的Timestamp和$ group上匹配$。 典型的生产查询将是30天的数据。目前我的收藏只有15天的数据。我的目标是获得30天数据的亚秒响应时间

顺便说一下,我今天做了一些分析 我把碎片转储了,然后在MacBook上安装的本地mongo中恢复。相同的查询仅用了2秒钟(在AWS isntance中花了4秒) 这没有任何意义,因为AWS实例至少比MacBook强大4倍(CPU和内存) MacBook Air - http://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i5-4250U+%40+1.30GHz AWS m2.2xlarge实例 - http://www.cpubenchmark.net/cpu.php?cpu=Intel+Xeon+E5-2665+%40+2.40GHz

我怀疑碎片化是因为AWS mongo实例中的数据在过去15天内通过应用程序填充。所以我将AWS mongo上的转储作为单独的集合重新导入。对这个新系列的查询耗时2s,与MAcBook速度相当。碎片化是肯定的一个原因。我打算稍后对碎片做更多的研究。 尽管对碎片进行了碎片整理,但与我的MacBook相同的时间这一事实并不合理,因为AWS isntance强大了4倍。 然后我们查看了cpu利用率,发现mongod实例只使用一个CPU(满分4个)来执行查询。我们现在计划在每台机器上安装4个分片来解决这个问题。如果你看到更好的方法,请告诉我。

还有一件事,我知道我的查询必须扫描整个集合但是2秒扫描~200MB数据对我来说似乎非常高。是期待还是我遗失了什么?

1 个答案:

答案 0 :(得分:2)

我尝试的事情:

1)您以一种使分组变得非常困难的方式组织了您的数据。如果您按照以下方式组织文档,可能会获得更好的结果:

{
    ...
    "actionType" : [{k:1, v:66}, {k:47, v:66}, {k:42, v:72}, {k:46, v:130}]
}

这将允许您在' actionType.k'上创建索引。然后,您可以对该索引进行匹配,以将整个数据集减少为您希望此聚合所需的确切actionTypes,其中您的查询为:

db.action.aggregate([{$unwind: '$actionType'}, 
        {$group:{_id:'$actionType.k', t:{$sum:'$actionType.v'} } }]);
//output
{ "_id" : 46, "t" : 130 }
{ "_id" : 42, "t" : 72 }
{ "_id" : 47, "t" : 66 }
{ "_id" : 1, "t" : 66 }

然后在' actionType.k'上确保指数。如果您不打算过滤所有不同的键值,则索引将有很大帮助,具体取决于文档中键的密度。如果您计划对每个密钥求和,那么索引在这里不会有帮助。

2)在cron-job / setTimeout计划中映射减少和/或添加这些。同样,根据您的更新周期以及您在任何时候需要数据的准确程度,请设置如下内容:

  • 每小时处理所有“脏”的事情。结果
  • 将当前值添加到正在运行的总计
  • 标记为'清洁'

如果你只对这个数据库进行插入操作,那么这是有效的。

3)如果键值正在定期更改(更新而不是插入),那么您可能会更好地运行与更新主集合同时发生的更改日志插入。

db.changes.insert({key:44, change:2});
db.changes.insert({key:34, change:-2});

然后经常清空变化'集合将值汇总到不同的集合。