26 сентября 2016 в 17:07

Какая-такая Data? Или ещё раз про MapReduce

Big Fish Small Fry by John Pollack Если Вы последние 10 лет провели на удаленном острове, без интернета и в отрыве от цивилизации, то специально для Вас мы попытаемся еще раз рассказать про концепцию MapReduce. Введение будет небольшим, в объеме достаточном, для реализации концепции MapReduce в среде InterSystems Caché. Если же Вы не сильно далеко удалялись последние 10 лет, то сразу переходите ко 2ой части, где мы создаем основы инфраструктуры.



Давайте сразу определимся, я не являюсь большим поклонником MapReduce, о чем можно было догадаться по предыдущим моим статьям/переводам — Майкл Стоунбрейкер — "Hadoop на распутье" и "Утилиты командной строки могут быть в 235-раз быстрее вашего Hadoop кластера" [Если быть точнее, я не являюсь поклонником Java реализаций Hadoop MapReduce, но это уже личное]


В-любом случае, несмотря на все эти оговорки и недостатки, есть еще множество причин, которые заставляют вернуться к этой теме и попытаться реализовать MapReduce в другой среде и на другом языке. Все это мы озвучим позже, но до этого поговорим про BigData...


Когда Data большая, а когда маленькая?


Несколько лет назад все стали сходить с ума по BigData, никто правда не знал когда его маленькие данные становятся большими, и где тот предел, но все понимали что это модно, молодёжно и «так» надо делать. Время шло, кое-кто объявил, что BigData уже не buzzword (это довольно таки забавно, но Gartner реально убрал волевым решением BigData со своей кривой базвордов за 2016, обосновав это тем, что термин расщепился на другие). Вне зависимости от желания Gartner термин BigData еще среди нас, живее всех живых, и думаю самое время определиться с его пониманием.


Например, понимаем ли мы до конца, когда наши «не очень большие данные» превращаются в «БОЛЬШИЕ ДАННЫЕ»?


Наиболее конкретный (из разумных) ответов дал Дэвид Кантер, один из самых уважаемых экспертов по архитектуре процессоров в целом и x86 в частности1:



FWIW, когда я, работая в Интеле, перешел в аппаратную команду, работающую над «процессором следующего поколения» (don'task), то я начал с изучения материалов про архитектуру процессора Nehalem на сайте Дэвида Кантера, а не с внутренних доков HAS и MAS. Потому как у Дэвида было лучше, и понятнее.


Т.е. если у вас «всего пара терабайт» данных, то вы, скорее всего, сможете найти аппаратную конфигурацию серверной машины, достаточную для того, чтобы все данные поместились в памяти сервера (при достаточном, конечно, количестве денег и мотивации), и ваши данные еще не совсем Большие.


BigData начинаются когда такой подход с вертикальным масштабированием (нахождением «более лучшей» машины) перестают работать, т.к. с определенного размера данных вы уже не можете купить большей конфигурации, ни за какие (разумные) деньги. И надо начинать расти вширь.


Проще – лучше


Ок, определившись с какого размера у нас данные выросли до термина BigData, мы должны определиться с подходами, которые работают на больших данных. Одним из первых подходов, который начал массово применяться на больших данных был MapReduce. Существует множество альтернативных программных моделей, работающих с большими данными, которые могут даже оказаться лучше или гибче чем MapReduce, но тот, однозначно может считаться самым упрощенным, хотя может быть и не самым эффективным.


Более того, как только мы начинаем рассматривать какую-то программную платформу, или платформу баз данных, на предмет поддержки BigData, мы по умолчанию предполагаем, что MapReduce сценарий поддерживается на этой платформе внутренними или внешними утилитами.


Другими словами – без MapReduce ты не можешь утверждать, что твоя платформа поддерживает BigData!


ALARM – если вы все же были не на луне последние 10 лет, то можете смело проматывать рассказ про основы алгоритма MapReduce, скорее всего, вы уже в курсе. Для остальных мы попытаемся (еще раз) рассказать про то, с чего это все начиналось, и как этим всем можно воспользоваться в конце 2016 года. (Особенно на платформах, где MapReduce не поддерживается из коробки.)


Часто было замечено, что самый простой подход к решению задачи позволяет получить наилучше результаты, и остаётся жить в продукте на продолжительное время. Вне от оригинального плана авторов. Даже если, в итоге, он не оказывается самым эффективным, но в силу того, что сообщество уже его широко узнало, и все изучили, и он просто достаточно хорош и решает задачи. Примерно такой эффект и наблюдается с моделью MapReduce – будучи очень простым в основе своей, он по-прежнему широко используется даже после того, как оригинальные авторы декларировали его смерть.


Масштабирование в Caché


Исторически InterSystems Caché имел достаточно инструментов в своём арсенале, как для вертикального, так и горизонтального масштабирования. Как мы все знаем (грустный смайл) Caché это не только сервер баз данных, но и сервер приложений, который может использовать ECP (Enterprise Cache Protocol) для горизонтального масштабирования и высокой доступности.


Особенность ECP протокола – будучи сильно оптимизированным протоколом для когерентности доступа к одним и тем же данным на разных узлах кластера, сильно упирается в производительность write daemon на центральном узле сервера БД. ECP позволяет добавить дополнительные счетные узлы с ядрами процессоров, если нагрузка на write-daemon не очень высокая, но этот протокол не поможет отмасштабировать ваше приложение горизонтально, если каждый из вовлеченных узлов порождает большую активность на запись. Дисковая подсистема на сервере БД по-прежнему будет узким местом.


На самом деле, при работе с большими данными современные приложения предполагают использование другого, или даже ортогонального озвученном выше, подхода. Масштабировать приложение горизонтально надо с использованием дисковой подсистемы на каждом из узлов кластера. В отличие от ECP, где данные приносятся на удаленный узел, мы наоборот, приносим код, размер которого предполагается малым, к данным на каждом узле, размер которых предполагается очень большим (как минимум относительно размера данных). Похожий тип партиционирования, именуемый шардингом, в будущем будет реализован в SQL движке Caché в одном из будущих продуктов. Но даже сегодня, на имеющихся в платформе средствах, мы можем реализовать нечто простое, что позволило бы нам спроектировать горизонтально масштабируемую систему, с применением современных, «модных, молодежных» подходов. Например, с применением MapReduce…


Google MapReduce


Оригинальная реализация MapReduce была написана в Google на Си++, но так получилось, что широкое распространение парадигмы началось в индустрии только с реализации MapReduce от Apache, которая на Java. В-любом случае, вне зависимости от языка реализации, идея остается одной и той же, будь та реализована на C++, Java, Go, или Caché ObjectScript, как в нашем случае.


[Хотя, для Caché ObjectScript реализации мы воспользуемся парой трюков, доступных только при операциях с многомерными массивами, известными как глобалы. Просто, потому что можем]



Рисунок 1. Исполнениевсреде MapReduce изстатьи "MapReduce: Simplified Data Processing on Large Clusters", OSDI-2004


Давайте пройдемся по стадиям алгоритма MapReduce, нарисованного в картинке выше:


  1. На входе у нас есть набор «файлов», или потенциально бесконечный поток данных, который мы можем разбить (партиционировать) на несколько независимых кусков данных;


  2. Также имеем набор параллельных исполнителей (локальных внутри узла или может быть удаленных, на других узлах кластера) которые мы можем назначить как обработчиков входных кусков данных (стадия «отображение» /« map »)


  3. Эти параллельные обработчики читают входной поток данных и выводят в выходной поток пару(ы) «ключ-значение». Выходной поток может быть записан в выходные файлы или куда-то еще (например, в кластерную файловую систему Google GFS, Apache HDFS, или в какое другое «волшебное место» реплицирующее данные на несколько узлов кластера);


  4. На следующей стадии, именуемой «свертка» / « reduce » у нас имеется другой набор обработчиков, которые занимаются (сюрприз) … сверткой. Они читают, для заданного ключа, всю коллекцию данных, и выводят результирующие данные как очередные «ключ-значения». Выходной поток этой стадии, аналогично предыдущей стадии, записывается в волшебные кластерные файловые системы или их аналоги.

Заметим, что MapReduce подход – пакетный по своей природе. Он не очень хорошо обрабатывает бесконечные потоки входных данных, в силу пакетной реализации, и будет ожидать завершения работы на каждой из его стадий («отображение» или «свертка»), перед тем как продвинуться дальше в конвейере. Этим он отличается от более современных поточных алгоритмов, используемых, например, в Apache Kafka, которые по своему дизайну нацелены на обработку «бесконечных» входных потоков.


Знающие люди пропустили данный раздел, а незнающие, думаю, по-прежнему смущены. Давайте рассмотрим классический пример word-count (подсчет слов в потоке данных), который по традиции используется при объяснении реализации MapReduce на разных языках программирования, и в разных средах.


Итак, допустим, нам надо подсчитать количество слов во входной коллекции (достаточно большой) файлов. Для ясности определимся, что словом будем считать последовательность символом между пробельными символами, т.е. цифры, знаки пунктуации также посчитаются частью слова, это, конечно, не очень хорошо, но в рамках простого примера это нас не волнует.


Будучи Си++ разработчиком в глубинах своей души, для меня алгоритм становится ясен, когда я вижу пример на Си++. Если «Вы — не такой», то не расстраивайтесь, скоро мы его покажем в упрощенном виде.


#include "mapreduce/mapreduce.h"

// User's map function
class WordCounter : public Mapper {
public:
    virtual void Map(const MapInput& input) {
        const string& text = input.value();
        const int n = text.size();
        for (int i = 0; i < n; ) {
            // Skip past leading whitespace
            while ((i < n) && isspace(text[i]))
                i++;
            // Find word end
            int start = i;
            while ((i < n) && !isspace(text[i]))
                i++;
            if (start < i)
                Emit(text.substr(start,i-start),"1");
        }
    }
};
REGISTER_MAPPER(WordCounter);

// User's reduce function
class Adder : public Reducer {
    virtual void Reduce(ReduceInput* input) {
        // Iterate over all entries with the
        // same key and add the values
        int64 value = 0;
        while (!input->done()) {
            value += StringToInt(input->value());
            input->NextValue();
        }
        // Emit sum for input->key()
        Emit(IntToString(value));
    }
};
REGISTER_REDUCER(Adder);

int main(int argc, char** argv) {
    ParseCommandLineFlags(argc, argv);
    MapReduceSpecification spec;

    // Store list of input files into "spec"
    for (int i = 1; i < argc; i++) {
        MapReduceInput* input = spec.add_input();
        input->set_format("text");
        input->set_filepattern(argv[i]);
        input->set_mapper_class("WordCounter");
    }

    // Specify the output files:
    // /gfs/test/freq-00000-of-00100
    // /gfs/test/freq-00001-of-00100
    // ...
    MapReduceOutput* out = spec.output();
    out->set_filebase("/gfs/test/freq");
    out->set_num_tasks(100);
    out->set_format("text");
    out->set_reducer_class("Adder");

    // Optional: do partial sums within map
    // tasks to save network bandwidth
    out->set_combiner_class("Adder");

    // Tuning parameters: use at most 2000
    // machines and 100 MB of memory per task
    spec.set_machines(2000);
    spec.set_map_megabytes(100);
    spec.set_reduce_megabytes(100);

    // Now run it
    MapReduceResult result;
    if (!MapReduce(spec, &result)) abort();
    // Done: 'result' structure contains info
    // about counters, time taken, number of
    // machines used, etc.
    return 0;
}

  • Программа, приведенная выше, вызывается со списком файлов, которые надо обработать, переданным через стандартные argc/argv.


  • Объект MapReduceInput инстанциируется как обертка для доступа к каждому файлу из входного списка и планируется на исполнение классом WordCount для обработки его данных;


  • MapReduceOutput инстанциируется с перенаправлением выходных данных в кластерную файловую систему GoogleGFS (обратите внимание на /gfs/test/*)


  • Классы Reducer (свёртщик, хмм) и Combiner (комбинатор) реализуются Си++ классом Adder, текст которого приводится в этой же программе;


  • Функция Map в классе Mapper, реализованная в нашем случае в классе WordCouner, получает данные через обобщенный интерфейс MapInput. Нашем случае этот интерфейс будет поставлять данные из файлов. Класс, реализующий данный интерфейс, должен реализовать метод value(), поставляющий следующую строку как string, и длину входных данных в методе size();


  • В рамках решения нашего задания, подсчета количества слов в файле, мы будем игнорировать пробельные символы и считать все остальное, между пробелами как отдельное слово (вне зависимости от знаков пунктуации). Найденное слово пишем в выходной «поток» через вызов функции Emit(word, "1");


  • Функция Reduce в классе реализации интерфейса Reducer (в нашем случае это Adder) получает свои входные данные через другой обобщенный интерфейс ReduceInput. Данная функция будет вызвана для определенного ключа (слова из файла, в нашем случае) из пары «ключ-значение», записанных на предыдущей стадии Map. Эта функция будет вызвана для обработки коллекции значений, советующих данному ключу (в нашем случае для последовательности «1»). В рамках нашего задания, ответственность функции Reducer — подсчитать количество таких единиц на входе и выдача суммарного числа в выходной канал.


  • Если у нас построен кластер из нескольких узлов, или просто запускается множество обработчиков в рамках алгоритма MapReduce, то ответственностью «мастера» будет разбить поток выдаваемых пар «ключ-значение» на соответствующие коллекции, и перенаправление этих коллекций на вход Reduce обработчикам.

Детали реализации такого мастер узла будут сильно зависеть от протокола реализации используемой технологии кластеризации, т.ч. мы опустим подробный рассказ об этом за скобками текущего повествования. В нашем случае, для Caché ObjectScript, для некоторых рассматриваемых алгоритмов (как текущий WordCount) мастер может быть реализован тривиально, в силу использования глобалов и их природы, как отсортированных, но разреженных массивов. О чем подробнее позже.


  • В общем случае, часто необходимо завести несколько шагов Reduce, например, для случаев, когда невозможно обработать полную коллекцию значений за один заход. И тогда появляется дополнительная(ые) стадия(ии) Combiner, которые будут дополнительно агрегировать результаты данных с предыдущих стадий Reduce.

Если, после такого подробного описания Си++ реализации, вам по-прежнему непонятно что такое MapReduce, то давайте попробуем изобразить этот алгоритм на нескольких строках одного известного скриптового языка:


map(String key, String value):
   // key: document name
   // value: document contents
   for each word w in value:
      EmitIntermediate(w, "1");

reduce(String key, Iterator values):
   // key: a word
   // values: a list of counts
   int result = 0;
   for each v in values:
      result += ParseInt(v);
   Emit(AsString(result));

Как в этом упрощенном примере видим, ответственностью функции map будет выдать последовательность пар <ключ, значение>. Эти пары перемешиваются и сортируются в мастере, и результирующие коллекции значений, для заданного ключа, отсылаются на вход функций reduce (свертка), которые, в свою очередь, ответственны за генерацию выходной пары <ключ, значение>. В нашем случае это будет <слово, счетчик>


В классической реализации MapReduce трансформация коллекции пар <ключ, значение> в раздельные коллекции <ключ, значени(я)> является самой время- и ресурсоёмкой операцией. В случае же Caché реализации, как из-за природы реализации хранилищ btree*, так и связующего протокола ECP, сортировка и агрегация на мастере становятся не такой большой задачей, реализуемой почти на автомате, почти «забесплатно». Об этом мы расскажем при случае в следующих статьях.


Пожалуй, этого достаточно для вводной части – мы еще не затронули собственно Caché ObjectScript реализации, хотя и дали достаточно информации для начала реализации MapReduce на любом языке. К нашей реализации MapReduce мы вернемся в следующей статье. Оставайтесь на линии!

Автор: @tsafin
InterSystems
рейтинг 141,29
Вендор: СУБД Caché, OLAP DeepSee, шина Ensemble

Комментарии (15)

  • +2
    Оригинальная реализация MapReduce была написана в Google на Си++, но так получилось, что широкое распространение парадигмы началось в индустрии только с реализации MapReduce от Apache, которая на Java.

    Вероятно, дело не в языке, а в том, что Google никогда не открывал исходников MapReduce. Поэтому пришлось ждать, пока кто-нибудь воплотит идею в open-source.

    • +2
      Конечно же речь о самой парадигме, и путях прихода её в индустрию. Но так уж получилось, что в 2004 году речь шла о Си++ реализации в Google, а в 2006 Hadoop был выпущен с Java реализацией. Вполне, даже, допускаю, что Яндекс свою Си++ реализацию написл примерно в тот же временной период.
      Но, к сожалению, ни Google, ни Яндекс, не опубликовали Си++ реализации, и народу оставалось играться на Java. (Вы только представьте себе, какой другой, «более лучшей» была бы индустрия HPC/BigData если бы изначально у народа были бы доступны C++ библиотеки для масштабирования bigdata решений. Но, история не терпит сослагательного наклонения, и мы рабоиаем на чем работаем)
      • +2
        Как известно, «хороша ложка к обеду», и, по счастливому стечению обстоятельств, maxim_babenko из Яндекса сегодня опубликовал описание их системы MapReduce https://habrahabr.ru/company/yandex/blog/311104/
        • +1
          Только «хороша ложка к обеду» вроде не про MapReduce? В том смысле, что вроде бы MapReduce «уже все»?
          • 0
            Почему всё? Слухи и смерти MapReduce слегка преувеличены. И пока за ним такие большие игроки, как Яндекс, пусть и с изменениями и улучшениями, парадигма будет жить.

            Здесь же («хороша ложка к обеду») речь шла про то, как плотно пошли статьи, моя — вчера, и из Яндекса сегодня. Что, опять же, свидетельствует о том, что технология еще актуальна и о ней говорят и её применяют.
    • +1
      И, раз пошел такой разговор, а почему, кстати, вы (Гугл) не опубликовали эту библиотеку ни тогда ни сейчас?
      • +3
        вы (Гугл)

        Мои комментарии выражают только моё личное мнение и не имеют никакого отношения к мнению компании.


        почему не опубликовали эту библиотеку ни тогда ни сейчас?

        Потому что это не библиотека, это сервис. Чтобы он хорошо работал и был удобным, нужен огромный кусок инфраструктуры: планировщик задач, система управления ресурсами, система развёртывания приложений, распределённая файловая система, системы мониторинга и половина стандартной библиотеки впридачу. В итоге получается, что нужно открыть огромную часть инфраструктуры и кода. Это непростое решение.

        • +1
          Потому что это не библиотека, это сервис. Чтобы он хорошо работал и был удобным, нужен огромный кусок инфраструктуры: планировщик задач, система управления ресурсами, система развёртывания приложений, распределённая файловая система, системы мониторинга и половина стандартной библиотеки впридачу.


          Вот, кстати, нет. Это именно библиотека, которая может основываться на инфраструктуре (определенная распределенная файловая система, или развертывание приложений), а может быть и абстрагиована от всего этого. Как я покажу в остальных статьях серии, MapReduce остаётся MapReduce дже если у него вынуть GFS или HDFS и пользовться другими средствами доставки данных в кластере.

          (В-общем и целом, если бы Google выложил MapReduce только как библиотеку, то сообщество нашло бы чем заменить инфраструктуру. Но чего уж тут. Нет так нет)
          • +3
            Это именно библиотека

            Ну понятно, что любой сервис можно оформить в виде библиотеки и исполняемого файла, абстрагировать библиотеку от внешних зависимостей, выделив интерфейсы для коммуникации и определив семантику и ожидаемую производительность каждого вызова каждого метода каждого интерфейса. Проблема в том, что если это не было сделано изначально, то обычно проще переписать всё с нуля, чем сделать такой рефакторинг. К тому же, чтобы правильно выделить абстракции, обычно нужно иметь в виду как минимум несколько возможных реализаций каждой абстракции (хотя бы три).


            Далее, чтобы пользователь мог использовать эту библиотеку, нужно попросить его реализовать все зависимости в соответствии с их семантикой. Это много работы.


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

  • 0
    Я прочитал вашу статью, представил ваши примеры, и у меня возник вопрос начинающего.

    Предположим, что мы в полном соответствии с примером в статье хотим посчитать повторения слов в следующем известном шуточном стихотворении:
    Ехал Гитлер через Гитлер
    Видит Гитлер Гитлер Гитлер
    Сунул Гитлер в Гитлер Гитлер
    Гитлер Гитлер Гитлер Гитлер

    Размер чанка примем равным 8 байтам, и для простоты будем считать, что текст у нас в какой-то однобайтовой кодировке, поэтому 1 байт = 1 знак, и вместо переносов строки — обычные пробелы.

    Если я все правильно понимаю, то это значит, что мапперы на входе получат следующее разбиение:
    0:  "Ехал Гит"
    1:  "лер чере"
    2:  "з Гитлер"
    3:  " Видит Г"
    4:  "итлер Ги"
    5:  "тлер Гит"
    6:  "лер Суну"
    7:  "л Гитлер"
    8:  " в Гитле"
    9:  "р Гитлер"
    10: "Гитлер Г"
    11: "итлер Ги"
    12: "тлер Гит"
    13: "лер"
    

    Теперь мапперы прогоняют свой алгоритм для каждого чанка и выдают следующие ключи:
    0:
    	"Ехал": 1
    	"Гит": 1
    1:
    	"лер": 1
    	"чере": 1
    2:
    	"з": 1
    	"Гитлер": 1
    3:
    	"Видит": 1
    	"Г": 1
    4:
    	"итлер": 1
    	"Ги": 1
    5:
    	"тлер": 1
    	"Гит": 1
    6:
    	"лер": 1
    	"Суну": 1
    7:
    	"л": 1
    	"Гитлер": 1
    8:
    	"в": 1
    	"Гитле": 1
    9:
    	"р": 1
    	"Гитлер": 1
    10:
    	"Гитлер": 1
    	"Г": 1
    11:
    	"итлер": 1
    	"Ги": 1
    12:
    	"тлер": 1
    	"Гит": 1
    13:
    	"лер": 1
    

    Наверное, вы уже поняли, что меня смущает, но я доведу мысль до конца. Теперь, перед тем, как передать данные в reducer-ы, платформа выполняет группировку по ключу.
    "Видит": 1
    "Г": 1, 1
    "Ги": 1, 1
    "Гит": 1, 1, 1
    "Гитле": 1
    "Гитлер": 1, 1, 1, 1
    "Ехал": 1
    "Суну": 1
    "в": 1
    "з": 1
    "итлер": 1, 1
    "л": 1
    "лер": 1, 1, 1
    "р": 1
    "тлер": 1, 1
    "чере": 1
    

    Ну и наши reducer-ы просто суммируют единички для каждого слова.

    Мне кажется проблемой то, что мы, по моему пониманию, решили не ту задачу, которую хотели. Мы получили вот это:
    "Видит": 1
    "Г": 2
    "Ги": 2
    "Гит": 3
    "Гитле": 1
    "Гитлер": 4
    "Ехал": 1
    "Суну": 1
    "в": 1
    "з": 1
    "итлер": 2
    "л": 1
    "лер": 3
    "р": 1
    "тлер": 2
    "чере": 1
    

    А хотели (я хотел) вот это:
    "Видит": 1
    "Гитлер": 12
    "Ехал": 1
    "Сунул": 1
    "в": 1
    "через": 1
    

    Если варьировать размер блока, результаты тоже будут дико гулять, и все еще не иметь ничего общего с правильным ответом, полученным традиционным способом. Что я понял неправильно? Почему так получается? Как с таким бороться?
    • 0
      А зачем делать размер чанка 8 байт? Почему весь исходный текст («файл») не был чанком?
      • 0
        Ну, у нас же big data, правильно? Пусть исходный текст является повторением вот той строки 1 триллион раз, т.е. итоговый размер структуры будет составлять 1.09e+14 байт, или порядка 99 ТиБ. Т.е. в память мы эту строку запихнуть точно не сможем. При этом ожидаемый выхлоп алгоритма должен быть
        "Видит": 1000000000000
        "Гитлер": 12000000000000
        "Ехал": 1000000000000
        "Сунул": 1000000000000
        "в": 1000000000000
        "через": 1000000000000
        

        А по факту будет отчет с разными комбинациями слов, разрезанных пополам в произвольных позициях. И даже если увеличить размер чанка до, например, 4 ГиБ, которые поместятся в памяти произвольной ноды, у нас все равно есть слова на границах, которые были разрезаны. Или я все же упускаю что-то в понимании?
        • 0
          БигДата в данном случе не бесконечной длины поток сознания, а большое количество файлов конечного размера (пусть и достаточно большого). При создании системы надо попытаться сбалансировать количество параллельных обработчиков, размер чанка (чем больше тем лучше, но не больше чем), и накладные расходы на планировщик (при маленьких чанках у нас не останется времени на полезную работу — одни функции планировщика и мастера).
          Здравый смысл подсказывает, что если входные данные расположены в файлах, то проще всего отдавать отдельному обработчику на стадии отображения файл полностью.
          (Главное, не пытаться запихнуть весь файл в память, ни к чему это.)
          • 0
            Ну и я тут говорю не о бесконечном потоке сознания, а о вполне конечном огромном файле размером в 99 ТиБ, который содержит какой-то текст — возможно, про Гитлера, возможно, Lorem ipsum, или вообще краткое содержание Википедии.

            Просто верно ли я понимаю, что mapreduce не приспособлен к работе с неструктурированными данными? И в данном конкретном случае, нельзя получить корректный результат без предварительной обработки и разбиения мегафайла на записи приемлемого размера, которые могут поместиться в память? И при этом мы можем выбрать только такой размер чанка, который кратен размеру записи?
            • 0
              Давайте вернемся к основам: парадигма MapReduce работает в пакетном (batch) режиме. Пока текущая стадия не закончит обработку, следующая стадия конвейера не запустится. Для очень больших объемов чанка вы такого не хотите. Вы хотите много, разумного размера чанков, или, если нет возможности нарезать данные на чанки, то вы хотите поточный режим. Это уже не MapReduce, т.е. как минимум не его оригинальная реализация.
              Если нужна поточная реализация (и вы пишете на Java) то вам в Spark Streaming или Apache Storm. Если вы про Си++, то увы, и MapReduce реализации у вас нет, не говоря про streaming реализацию (ждем публичного релиза Yandex YT как-нибудь в будущем, тогда появится всё). Если же вы про InterSystems Caché ObjectScript, то продолжайте читать данную серию. (Вторая часть, третья часть) Там будет и поточный режим.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка