【问题标题】:MongoDB MapReduce is much slower than pure Java processing?MongoDB MapReduce 比纯 Java 处理慢很多?
【发布时间】:2010-12-13 15:26:53
【问题描述】:

我想计算一个集合中我的文档(包括嵌入的)的所有键。 首先,我编写了一个 Java 客户端来解决这个问题。显示结果不到 4 秒。 然后我写了一个 map/reduce 函数。结果很好,但运行该功能需要 30 多秒! 我认为 map/reduce 函数会更快,因为它是在服务器端执行的。 Java 客户端需要从服务器获取每个文档,但速度要快得多。 为什么会这样?

//这是我的地图功能:

map = function(){
    for(var key in this) {
      emit(key, {count:1});
      if(isNestedObject(this[key])){
        m_sub(key, this[key]);
      }
    }
}

//这是我的reduce函数:

reduce = function (key, emits) {
    total = 0;
    for (var i in emits) {
        total += emits[i].count;
    }
    return {count:total};
}

//这里是对mapreduce的调用:

mr = db.runCommand({"mapreduce":"keyword", "map" : map, "reduce" : reduce, 
    "scope":{
        isNestedObject : function (v) {
            return v && typeof v === "object";
        },
        m_sub : function(base, value) {
            for(var key in value) {
              emit(base + "." + key, {count:1});
              if(isNestedObject(value[key])){
                m_sub(base + "." + key, value[key]);
              }
            }
        }
    }
})

//这里是输出:

{
 "result" : "tmp.mr.mapreduce_1292252775_8",
 "timeMillis" : 39087,
 "counts" : {
  "input" : 20168,
  "emit" : 986908,
  "output" : 1934
 },
 "ok" : 1
}

//这是我的Java客户端:

public static Set<String> recursiv(DBObject o){

        Set<String> keysIn = o.keySet();
        Set<String> keysOut = new HashSet<String>();
        for(String s : keysIn){
            Set<String> keys2 = new HashSet<String>();
            if(o.get(s).getClass().getSimpleName().contains("Object")){
                DBObject o2 = (DBObject) o.get(s);
                keys2 = recursiv(o2);
                for(String s2 : keys2){
                    keysOut.add(s + "." + s2);
                }   
            }else{
                keysOut.add(s);
            } 
        }
        return keysOut;     
    }

    public static void main(String[] args) throws Exception {

        final Mongo mongo =  new Mongo("xxx.xxx.xxx.xxx");
        final DB db = mongo.getDB("keywords");
        final DBCollection keywordTable = db.getCollection("keyword");
        Multiset<String> count = HashMultiset.create();

        long start = System.currentTimeMillis();

        DBCursor curs = keywordTable.find();    
        while(curs.hasNext()){
            DBObject o = curs.next();
            Set<String> keys = recursiv(o);
            for(String s : keys){
                count.add(s);
            }
        }

        long end = System.currentTimeMillis();
        long duration = end - start;

        System.out.println(new SimpleDateFormat("mm:ss:SS").format(Long.valueOf(duration)));              
        System.out.println("duration:" + duration + " ms");
        //System.out.println(count);
        System.out.println(count.elementSet().size());

    }

//这里是输出:

00:03:726
duration:3726 ms
1898

不必担心结果数量不同(1934 年与 1898 年)。这是因为 map reduce 还计算了数组中的键,这些键不被 java 客户端计算在内。 感谢您阐明不同的执行时间。

【问题讨论】:

    标签: java performance mongodb mapreduce


    【解决方案1】:

    这不是一个很好的答案,但是在 o'reilly mongo 的书中,kristina 说 map-reduce 查询是您可以做的最慢的事情之一,但它们也是最灵活和最具可扩展性的. Mongo 将能够拆分查询并处理所有节点的处理能力,这意味着您应该通过添加的每个节点获得线性可伸缩性。但是在单个节点上,即使是 group by 查询也会比 map reduce 更快。

    【讨论】:

    • 正如马特所说。当您的 map reduce 操作的空间需求超过可用内存量时,我会更多地关注 mongo map-reduce。它们涉及创建和填充临时集合以创建结果(并在 javascript 中执行),它们将比 JVM 上的直接计算慢。
    • 感谢您的回复!我对以下 jira 得出了相同的结论,据报道,使用 mongo 的 map reduce 的性能比 python 差 350 倍:jira.mongodb.org/browse/SERVER-1197 答案在这里同一个方向:stackoverflow.com/questions/2599943/2613106#2613106
    • 显然 MongoDB map-reduce 仍在单线程运行。难怪它不扩展。它并不是真正意义上的高度并行的 map-reduce。
    【解决方案2】:

    另一个原因是 mongodb 的 javascript 引擎存在问题,它只允许他们使用一个单线程。 Mongodb 计划切换到 google 的 v8 javascript 引擎,希望允许 mongodb 处理 map/reduce 多线程。见http://www.mongodb.org/display/DOCS/MapReduce#MapReduce-Parallelismhttps://jira.mongodb.org/browse/SERVER-2407

    【讨论】:

      【解决方案3】:

      如果可以,您应该查看聚合框架命令。不如 MapReduce 灵活,但性能令人印象深刻。我用它把大量的集合数据聚合成每小时、每天、每月的摘要,在我们的情况下,使用 MapReduce 的性能比超过 1 到 50。

      我们选择了具有相同结构的分段集合设计,这使我们能够运行小而多的聚合作业,聚合命令的管道概念效果很好。

      我还发现 $group 命令非常高效,但对大小和分片的限制限制了它的使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-12
        • 1970-01-01
        • 1970-01-01
        • 2016-09-09
        相关资源
        最近更新 更多