当前位置:编程学习 > C/C++ >>

MongoDB MapReduce 性能提升20倍的优化宝典

自从MongoDB被越来越多的大型关键项目采用后,数据分析也成为了越来越重要的话题。人们似乎已经厌倦了使用不同的软件来进行分析(这都利用到了Hadoop),因为这些方法往往需要大规模的数据传输,而这些成本相当昂贵。

MongoDB提供了2种方式来对数据进行分析:Map Reduce(以下简称MR)和聚合框架(Aggregation Framework)。MR非常灵活且易于使用,它可以很好地与分片(sharding)结合使用,并允许大规模输出。尽管在MongoDB v2.4版本中,由于JavaScript引擎从Spider切换到了V8,使得MR的性能有了大幅改进,但是与Agg Framework(使用C++)相比,MR的速度还是显得比较慢。本文就来看看,有哪些方法可以让MR的速度有所提升。

测试

首先我们来做个测试,插入1000万文档,这些文档中包含了介于0和100万之间的单一整数值,这意味着,平均每10个文档具有相同的值。

代码
> for (var i = 0; i < 10000000; ++i){ db.uniques.insert({ dim0: Math.floor(Math.random()*1000000) });} 
> db.uniques.findOne() 
{ "_id" : ObjectId("51d3c386acd412e22c188dec"), "dim0" : 570859 } 
> db.uniques.ensureIndex({dim0: 1}) 
> db.uniques.stats() 

        "ns" : "test.uniques", 
        "count" : 10000000, 
        "size" : 360000052, 
        "avgObjSize" : 36.0000052, 
        "storageSize" : 582864896, 
        "numExtents" : 18, 
        "nindexes" : 2, 
        "lastExtentSize" : 153874432, 
        "paddingFactor" : 1, 
        "systemFlags" : 1, 
        "userFlags" : 0, 
        "totalIndexSize" : 576040080, 
        "indexSizes" : { 
                "_id_" : 324456384, 
                "dim0_1" : 251583696 
        }, 
        "ok" : 1 


这里我们想要得到文档中唯一值的计数,可以通过下面的MR任务来轻松完成:

代码
> db.runCommand( 
{ mapreduce: "uniques", 
map: function () { emit(this.dim0, 1); }, 
reduce: function (key, values) { return Array.sum(values); }, 
out: "mrout" }) 

        "result" : "mrout", 
        "timeMillis" : 1161960, 
        "counts" : { 
                "input" : 10000000, 
                "emit" : 10000000, 
                "reduce" : 1059138, 
                "output" : 999961 
        }, 
        "ok" : 1 


正如你看到的,输出结果大约需要1200秒(在EC2 M3实例上测试),共输出了1千万maps、100万reduces、999961个文档。结果类似于:

代码
> db.mrout.find() 
{ "_id" : 1, "value" : 10 } 
{ "_id" : 2, "value" : 5 } 
{ "_id" : 3, "value" : 6 } 
{ "_id" : 4, "value" : 10 } 
{ "_id" : 5, "value" : 9 } 
{ "_id" : 6, "value" : 12 } 
{ "_id" : 7, "value" : 5 } 
{ "_id" : 8, "value" : 16 } 
{ "_id" : 9, "value" : 10 } 
{ "_id" : 10, "value" : 13 } 
... 


下面就来看看如何进行优化。

使用排序

我在之前的这篇文章中简要说明了使用排序对于MR的好处,这是一个鲜为人知的特性。在这种情况下,如果处理未排序的输入,意味着MR引擎将得到随机排序的值,基本上没有机会在RAM中进行reduce,相反,它将不得不通过一个临时collection来将数据写回磁盘,然后按顺序读取并进行reduce。

下面来看看如果使用排序,会有什么帮助:

代码
> db.runCommand( 
{ mapreduce: "uniques", 
map: function () { emit(this.dim0, 1); }, 
reduce: function (key, values) { return Array.sum(values); }, 
out: "mrout", 
sort: {dim0: 1} }) 

        "result" : "mrout", 
        "timeMillis" : 192589, 
        "counts" : { 
                "input" : 10000000, 
                "emit" : 10000000, 
                "reduce" : 1000372, 
                "output" : 999961 
        }, 
        "ok" : 1 


现在时间降到了192秒,速度提升了6倍。其实reduces的数量是差不多的,但是它们在被写入磁盘之前已经在RAM中完成了。

使用多线程

在MongoDB中,一个单一的MR任务并不能使用多线程——只有在多个任务中才能使用多线程。但是目前的多核CPU非常有利于在单一服务器上进行并行化工作,就像Hadoop。我们需要做的是,将输入数据分割成若干块,并为每个块分配一个MR任务。splitVector命令可以帮助你非常迅速地找到分割点,如果你有更简单的分割方法更好。

代码
> db.runCommand({splitVector: "test.uniques", keyPattern: {dim0: 1}, maxChunkSizeBytes: 32000000}) 

    "timeMillis" : 6006, 
    "splitKeys" : [ 
        { 
            "dim0" : 18171 
        }, 
        { 
            "dim0" : 36378 
        }, 
        { 
            "dim0" : 54528 
        }, 
        { 
            "dim0" : 72717 
        }, 
… 
   &n

补充:软件开发 , C++ ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,