分析MongoDb聚合性能

我试图从集合中获得聚合的毫秒(< second)响应时间.但是,即使数据量很小(约200MB),它目前也需要3-5秒.我对此集合的预期生产数据大约为每个碎片100GB.

我已经检查了以下内容

– 当我在每个分片上单独尝试查询时,响应时间相同.

– 检查了分析输出,我所能看到的只是高timeLockedMicros和numYield.

– 在MMS图表中也找不到任何异常.

我觉得这里有些愚蠢的东西.任何帮助进一步分析这一点非常感谢.我的群集和收集详细信息如下

集群 – 34个节点,34 GB,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" : {

    },

UPDATE
谢谢你的快速回应. Apreciate它.我喜欢你的新数据模型和索引.但是,我担心这不适合我目前的数据,因为,
– 99%的记录将具有actionType.1和的soem值
– 99%的查询将选择actionType.1
因此,actiionType.K上的索引对我来说无济于事.

正如你在#2& amp; #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)您组织数据的方式使分组变得非常困难.如果您按照以下方式组织文档,可能会获得更好的结果:

{
    ...
    "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’上的ensureIndex.如果您不打算过滤所有不同的键值,索引将有很大帮助,具体取决于文档中键的密度.如果您计划对每个键求和,那么索引在这里将无济于事.

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

>每小时处理所有“脏”的结果
>将当前值添加到运行总计中
>标记为’干净’

如果你只对这个数据库进行插入操作,那就行了.

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

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

然后通常清空“更改”集合,将值汇总到不同的集合.

点赞