Статьи

Радость алгоритмов и NoSQL: пример MongoDB (Map-Reduce) (часть 2)

В первой части этой статьи описывается использование MongoDB для реализации расчетов молекулярных сходств. Во второй части обсуждается рефакторинг этого решения с использованием встроенной в MongoDB функциональности сокращения карт для повышения общей производительности.

В части 1 этой статьи, я описал использование MongoDB для решения конкретной хемоинформатики проблемы, а именно вычисления молекулярных сходства . В зависимости от целевого коэффициента Tanimoto, решение MongoDB может просматривать базу данных с миллионом соединений в течение секунды. Чтобы сделать это возможным, запросы возвращают только химические соединения, которые, теоретически, способны удовлетворить конкретную цель Tanimoto. Несмотря на то, что эта оптимизация имеет место, число соединений, возвращаемых этим запросом, значительно увеличивается, когда целевой Tanimoto понижается . Пример кода в репозитории GitHubнапример, импорт и индексы ~ 25000 химических соединений. Когда используется целевой Tanimoto 0,8 , запрос возвращает ~ 700 соединений. Когда целевой показатель Tanimoto снижается до 0,6 , количество возвращаемых соединений увеличивается до ~ 7000 . Используя функциональность объяснения MongoDB , можно заметить, что время выполнения внутреннего запроса MongoDB немного увеличивается по сравнению с накладными расходами на выполнение для передачи полного списка из 7000 соединений в удаленное приложение Java. Следовательно, было бы более целесообразно выполнять вычисления локально по отношению к месту хранения данных. Добро пожаловать во встроенную в MongoDB функциональность сокращения карт !

1. MongoDB карта молекулярного сходства — уменьшите запрос

Map-Reduction — это концептуальная структура, представленная Google, позволяющая обрабатывать огромные наборы данных с использованием большого количества узлов обработки . Общая идея состоит в том, что большая проблема будет разделена в наборе меньших подзадач , которые можно ответить (т.е. решить) с помощью отдельного узла обработки (далее карты шага ). После этого индивидуальных решений будут объединены снова , чтобы получить окончательный ответ на более серьезную проблему (The уменьшение шаги ). Убедившись, что индивидуальная карта и шаги сокращения могут быть вычисленынезависимо друг от друга, этот метод «разделяй и властвуй» можно легко распараллелить на кластере узлов обработки. Давайте начнем с рефакторинга нашего решения, чтобы использовать функциональность MongoDB для уменьшения карты.

// Calculate the essential numbers
int maxnumberofcompoundfingerprints = (int) (fingerprintsToFind.size() / 0.6);
int minnumberofcompoundfingerprints = (int) (fingerprintsToFind.size() * 0.6);
int numberoffingerprintstoconsider = fingerprintsToFind.size() - minnumberofcompoundfingerprints;

List
 
   fingerprintsToConsider = fingerprintsToFind.subList(0,numberoffingerprintstoconsider+1);

// Find all compounds that satisfy the specified conditions
DBObject compoundquery =   
    QueryBuilder.start(FINGERPRINTS_PROPERTY).in(fingerprintsToConsider)
                .and(FINGERPRINTCOUNT_PROPERTY).lessThanEquals(maxnumberofcompoundfingerprints)
                .and(FINGERPRINTCOUNT_PROPERTY).greaterThanEquals(minnumberofcompoundfingerprints)
                .get();

// The map fuction
String map = "function() {  " +
                 "var found = 0; " +
                 "var fingerprintslength = this.fingerprints.length; " +
                 "for (i = 0; i < fingerprintslength; i++) { " +
                     "if (fingerprintstofind[this.fingerprints[i]] === true) { found++; } " +
                 "} " +
                 "if (found >= minnumberofcompoundfingerprints) { emit (this.compound_cid, {found : found, total : this.fingerprint_count} ); } " +
             "}";

// Execute the map reduce function
MapReduceCommand mr = new MapReduceCommand(compoundsCollection, map, "", null, MapReduceCommand.OutputType.INLINE, compoundquery);

// Create a hashmap for the fingerprints to find (to speed up the javascript execution)
Map
  
    tofind = new HashMap
   
    ();
for(String fingerprinttofind : fingerprintsToFind) {
    tofind.put(fingerprinttofind,true);
}

// Set the map reduce scope
Map
    
      scope = new HashMap
     
      ();
scope.put("fingerprintstofind",tofind);
scope.put("minnumberofcompoundfingerprints",minnumberofcompoundfingerprints);
mr.setScope(scope);

// Execute the map reduce
MapReduceOutput out = compoundsCollection.mapReduce(mr);

// Iterate the results
for (DBObject result : out.results()) {
    String compound_cid = (String)result.get("_id");
    DBObject value = (DBObject)result.get("value");

    // Calculate the tanimoto coefficient
    double totalcount = (Double)value.get("total");
    double found = (Double)value.get("found");
    double tanimoto = (Double)value.get("found") / ((Double)value.get("total") + fingerprintsToFind.size() - (Double)value.get("found"));
    // We still need to check whether the tanimoto is really >= the required similarity
    if (tanimoto >= 0.6) {
        System.out.println(compound_cid + " " + (int)(tanimoto * 100) + "%");
    }
}

     
    
   
  
 

Карта шаг на карте-свертке осуществления того или MongoDB берет на MongoDB документа в качестве входных данных и выдает один (или более) ответы (которые, по сути, являются снова MongoDB документы). Выполнение нашего шага карты для всех составных документов в коллекции составов будет не очень эффективным. Вместо этого мы хотели бы ограничить выполнение нашего шага карты теми документами, которые теоретически могут соответствовать цели Tanimoto. К счастью, мы уже определили этот запрос, а именно составной запрос выбора, который был описан в первой части этой статьи! Используя этот запрос, только соединения, которые соответствуют этому запросу, проталкиваются через шаг карты. Функция карты (и уменьшения) MongoDB выражается черезJavaScript . В нашем случае мы рассчитываем количество уникальных шаблонов отпечатков пальцев, которые совместно используются как целевым, так и входным соединением. В случае достижения минимального количества шаблонов отпечатков пальцев , шаг карты генерирует документ, содержащий идентификатор PubChem (как идентификатор) и некоторую важную статистику (как значения). Уменьшения шага используется для совокупности ответов на конечный результат. Однако в нашем случае нас интересуют индивидуальные результаты для каждого соединения (документа). Следовательно, функция сокращения не применяется. При выполнении этой функции преобразования карты возвращается только 27 соединений (которые могут потенциально совпадать) вместо 7000 соединения при использовании предыдущего запроса Java!

Можно было бы ожидать , что время выполнения на карту-свертке запроса будет значительно быстрее по сравнению с раствором Java . К сожалению, это не так. Во-первых, интерпретируемый Javascript во много раз медленнее по сравнению с Java. Во-вторых, несмотря на то, что при уменьшении количества ядер ЦП этапы сокращения карт можно распараллелить, функция отображения карт MongoDB всегда выполняется однопоточным . Чтобы обойти это ограничение , можно использовать шардинг MongoDB . Проще говоря, вместо размещения всех данных на одном узле MongoDB, несколькоИспользуются узлы MongoDB, каждый из которых отвечает за хранение части общего набора данных . При выполнении нашей карты-свертке функции, каждый узел будет выполнять Map-Reduce шагов по его части данных параллельно . Следовательно, при использовании шардинга в кластере из 4 узлов MongoDB запрос map-Reduce выполняется почти в 4 раза быстрее, уже догнав производительность Java-решения. За исключением конфигурации сегментирования MongoDB , не требуется никаких изменений самой функции map-Reduce. Следовательно, горизонтальное масштабирование с MongoDB — это бриз …

2. Вывод

Производительность MongoDB по уменьшению карт немного разочаровывает. MongoDB в настоящее время рекомендует использовать его только для вычислений почти в реальном времени . Версия 2.0 MongoDB должна кардинально улучшить производительность, связанную с уменьшением карт, так как движок JavaScript будет заменен другими платформами исполнения. Тем не менее, производительность снижения карт в настоящее время может быть повышена путем распределения нагрузки на несколько сегментов MongoDB.