MongoDB MapReduce намного медленнее, чем чистая обработка Java?

Я хотел посчитать все ключи моих документов (включая встроенные) коллекции. Сначала я написал клиент 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 = 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). Это связано с тем, что карта сокращения подсчитывает также ключи в массиве, которые не подсчитываются java-клиентом. Спасибо, что пролили свет на разное время исполнения.


person Kay    schedule 13.12.2010    source источник


Ответы (3)


Это не такой уж и большой ответ, но в книге орейли Монго Кристина говорит, что запросы с уменьшением карты - одна из самых медленных вещей, которые вы можете сделать, но они также являются наиболее гибкими и наиболее масштабируемыми. Mongo сможет разбить запрос и обработать вычислительную мощность на всех узлах, что означает, что вы должны получить линейную масштабируемость с каждым добавляемым узлом. Но на одном узле даже группировка по запросу будет быстрее, чем сокращение карты.

person Matt Briggs    schedule 13.12.2010
comment
Как сказал Мэтт. Я бы больше посмотрел на mongo map-reduce, когда требования к пространству для вашей карты reduce превышают объем доступной памяти. Они включают создание и заполнение временной коллекции для создания результата (и выполняется в javascript), они будут намного медленнее, чем прямые вычисления на JVM. - person Michael; 14.12.2010
comment
Спасибо за ответ! Я пришел к такому же выводу в отношении следующей jira, где производительность, как сообщается, при использовании mongo map reduce в 350 раз хуже, чем python: jira.mongodb.org/browse/SERVER-1197 Ответ здесь идет в том же направлении: stackoverflow.com/questions/2599943/2613106#2613106 - person Kay; 14.12.2010
comment
Судя по всему, MongoDB map-reduce все еще работает в однопоточном режиме. Неудивительно, что он не масштабируется. На самом деле это не map-reduce, который должен быть очень параллельным. - person Has QUIT--Anony-Mousse; 16.06.2012

Другая причина в том, что у mongodb есть проблемы с движком javascript, который позволяет им использовать только один поток. Mongodb планирует перейти на движок google v8 javascript, который, как мы надеемся, позволяет mongodb обрабатывать карту / сокращать многопоточность. См. http://www.mongodb.org/display/DOCS/MapReduce#MapReduce-Parallelism и https://jira.mongodb.org/browse/SERVER-2407

person rompetroll    schedule 04.08.2011

Если можете, вам следует изучить команду фреймворка агрегации. Не такой гибкий, как MapReduce, но производительность впечатляет. Я использовал его для агрегирования большого количества данных коллекций в ежечасные, ежедневные, ежемесячные сводки, коэффициент производительности с MapReduce был больше 1 к 50 в нашей ситуации.

Мы выбрали дизайн с сегментированной коллекцией с идентичной структурой, что позволило нам выполнять небольшие, но многочисленные задания агрегирования, концепция конвейера команды агрегирования работает отлично.

Я также обнаружил, что команда $ group очень эффективна, но ограничение по размеру и осколкам ограничивает ее использование.

person user2892192    schedule 09.12.2013