Объединение нескольких заданий MapReduce в Hadoop


124

Во многих реальных ситуациях, когда вы применяете MapReduce, окончательные алгоритмы превращаются в несколько шагов MapReduce.

т.е. Map1, Reduce1, Map2, Reduce2 и так далее.

Итак, у вас есть результат последнего сокращения, который необходим в качестве входных данных для следующей карты.

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

Каков рекомендуемый способ сделать это в Hadoop?

Есть ли (простой) пример, который показывает, как правильно обрабатывать эти промежуточные данные, включая последующую очистку?


2
используя какой фреймворк mapreduce?
skaffman

1
Я отредактировал вопрос, чтобы уточнить, что я говорю о Hadoop.
Niels Basjes

Я бы порекомендовал для этого жемчужину свинопаса: github.com/Ganglion/swineherd best, Tobias
Tobias

Ответы:


57

Я думаю, что этот учебник в сети разработчиков Yahoo поможет вам в этом: Создание цепочки заданий

Вы используете расширение JobClient.runJob(). Путь вывода данных из первого задания становится входным путем для вашего второго задания. Их необходимо передавать в качестве аргументов вашим заданиям с соответствующим кодом для их анализа и настройки параметров для задания.

Я думаю, что вышеупомянутый метод может быть тем же способом, что и более старый API с отображением карт, но он все равно должен работать. В новом API mapreduce будет аналогичный метод, но я не уверен, что это такое.

Что касается удаления промежуточных данных после завершения задания, вы можете сделать это в своем коде. Раньше я использовал что-то вроде:

FileSystem.delete(Path f, boolean recursive);

Где путь - это расположение данных в HDFS. Убедитесь, что вы удаляете эти данные только тогда, когда они не требуются для других заданий.


3
Спасибо за ссылку на учебник Yahoo. Цепочка заданий - это действительно то, что вам нужно, если они выполняются одновременно. Я искал простой способ, если вы хотите иметь возможность запускать их по отдельности. В упомянутом руководстве я обнаружил SequenceFileOutputFormat «Записывает двоичные файлы, подходящие для чтения в последующие задания MapReduce» и соответствующий SequenceFileInputFormat, которые делают все это очень простым. Спасибо.
Niels Basjes

20

Есть много способов сделать это.

(1) Каскадирование рабочих мест

Создайте объект JobConf «job1» для первого задания и установите все параметры с «input» в качестве входного каталога и «temp» в качестве выходного каталога. Выполните эту работу:

JobClient.run(job1).

Сразу под ним создайте объект JobConf «job2» для второго задания и установите все параметры с «temp» в качестве входного каталога и «output» в качестве выходного каталога. Выполните эту работу:

JobClient.run(job2).

(2) Создайте два объекта JobConf и установите в них все параметры так же, как (1), за исключением того, что вы не используете JobClient.run.

Затем создайте два объекта Job с настройками jobconfs в качестве параметров:

Job job1=new Job(jobconf1); 
Job job2=new Job(jobconf2);

Используя объект jobControl, вы указываете зависимости задания, а затем запускаете задания:

JobControl jbcntrl=new JobControl("jbcntrl");
jbcntrl.addJob(job1);
jbcntrl.addJob(job2);
job2.addDependingJob(job1);
jbcntrl.run();

(3) Если вам нужна структура вроде Map + | Уменьшить | Map *, вы можете использовать классы ChainMapper и ChainReducer, которые поставляются с Hadoop версии 0.19 и новее.


7

На самом деле есть несколько способов сделать это. Я сосредоточусь на двух.

Один из них - через Riffle ( http://github.com/cwensel/riffle ) - библиотеку аннотаций для идентификации зависимых вещей и «выполнения» их в зависимости (топологическом) порядке.

Или вы можете использовать Cascade (и MapReduceFlow) в Cascading ( http://www.cascading.org/ ). В будущей версии будут поддерживаться аннотации Riffle, но теперь она отлично работает с необработанными заданиями MR JobConf.

Один из вариантов - вообще не управлять заданиями MR вручную, а разработать приложение с помощью Cascading API. Затем JobConf и объединение заданий обрабатываются внутри с помощью классов Cascading planner и Flow.

Таким образом, вы тратите свое время на решение своей проблемы, а не на механику управления заданиями Hadoop и т. Д. Вы даже можете накладывать поверх разных языков (например, clojure или jruby), чтобы еще больше упростить разработку и приложения. http://www.cascading.org/modules.html


6

Я выполнил цепочку заданий, используя объекты JobConf один за другим. Я взял пример WordCount для объединения заданий. Одна задача вычисляет, сколько раз слово a повторяется в данном выводе. Второе задание принимает выходные данные первого задания в качестве входных данных и вычисляет общее количество слов в заданных входных данных. Ниже приведен код, который необходимо поместить в класс Driver.

    //First Job - Counts, how many times a word encountered in a given file 
    JobConf job1 = new JobConf(WordCount.class);
    job1.setJobName("WordCount");

    job1.setOutputKeyClass(Text.class);
    job1.setOutputValueClass(IntWritable.class);

    job1.setMapperClass(WordCountMapper.class);
    job1.setCombinerClass(WordCountReducer.class);
    job1.setReducerClass(WordCountReducer.class);

    job1.setInputFormat(TextInputFormat.class);
    job1.setOutputFormat(TextOutputFormat.class);

    //Ensure that a folder with the "input_data" exists on HDFS and contains the input files
    FileInputFormat.setInputPaths(job1, new Path("input_data"));

    //"first_job_output" contains data that how many times a word occurred in the given file
    //This will be the input to the second job. For second job, input data name should be
    //"first_job_output". 
    FileOutputFormat.setOutputPath(job1, new Path("first_job_output"));

    JobClient.runJob(job1);


    //Second Job - Counts total number of words in a given file

    JobConf job2 = new JobConf(TotalWords.class);
    job2.setJobName("TotalWords");

    job2.setOutputKeyClass(Text.class);
    job2.setOutputValueClass(IntWritable.class);

    job2.setMapperClass(TotalWordsMapper.class);
    job2.setCombinerClass(TotalWordsReducer.class);
    job2.setReducerClass(TotalWordsReducer.class);

    job2.setInputFormat(TextInputFormat.class);
    job2.setOutputFormat(TextOutputFormat.class);

    //Path name for this job should match first job's output path name
    FileInputFormat.setInputPaths(job2, new Path("first_job_output"));

    //This will contain the final output. If you want to send this jobs output
    //as input to third job, then third jobs input path name should be "second_job_output"
    //In this way, jobs can be chained, sending output one to other as input and get the
    //final output
    FileOutputFormat.setOutputPath(job2, new Path("second_job_output"));

    JobClient.runJob(job2);

Команда для запуска этих заданий:

bin / hadoop jar TotalWords.

Нам нужно дать окончательное имя задания для команды. В приведенном выше случае это TotalWords.


5

Вы можете запустить цепочку MR, как указано в коде.

ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ : был предоставлен только код драйвера

public class WordCountSorting {
// here the word keys shall be sorted
      //let us write the wordcount logic first

      public static void main(String[] args)throws IOException,InterruptedException,ClassNotFoundException {
            //THE DRIVER CODE FOR MR CHAIN
            Configuration conf1=new Configuration();
            Job j1=Job.getInstance(conf1);
            j1.setJarByClass(WordCountSorting.class);
            j1.setMapperClass(MyMapper.class);
            j1.setReducerClass(MyReducer.class);

            j1.setMapOutputKeyClass(Text.class);
            j1.setMapOutputValueClass(IntWritable.class);
            j1.setOutputKeyClass(LongWritable.class);
            j1.setOutputValueClass(Text.class);
            Path outputPath=new Path("FirstMapper");
            FileInputFormat.addInputPath(j1,new Path(args[0]));
                  FileOutputFormat.setOutputPath(j1,outputPath);
                  outputPath.getFileSystem(conf1).delete(outputPath);
            j1.waitForCompletion(true);
                  Configuration conf2=new Configuration();
                  Job j2=Job.getInstance(conf2);
                  j2.setJarByClass(WordCountSorting.class);
                  j2.setMapperClass(MyMapper2.class);
                  j2.setNumReduceTasks(0);
                  j2.setOutputKeyClass(Text.class);
                  j2.setOutputValueClass(IntWritable.class);
                  Path outputPath1=new Path(args[1]);
                  FileInputFormat.addInputPath(j2, outputPath);
                  FileOutputFormat.setOutputPath(j2, outputPath1);
                  outputPath1.getFileSystem(conf2).delete(outputPath1, true);
                  System.exit(j2.waitForCompletion(true)?0:1);
      }

}

ПОСЛЕДОВАТЕЛЬНОСТЬ:

( JOB1 ) MAP-> REDUCE-> ( JOB2 ) MAP
Это было сделано для сортировки ключей, но есть и другие способы, такие как использование древовидной карты.
Тем не менее, я хочу сосредоточить ваше внимание на способе связывания заданий! !
Спасибо




3

Мы можем использовать waitForCompletion(true)метод задания, чтобы определить зависимость между заданиями.

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

public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub

        CCJobExecution ccJobExecution = new CCJobExecution();

        Job distanceTimeFraudJob = ccJobExecution.configureDistanceTimeFraud(new Configuration(),args[0], args[1]);
        Job spendingFraudJob = ccJobExecution.configureSpendingFraud(new Configuration(),args[0], args[1]);
        Job locationFraudJob = ccJobExecution.configureLocationFraud(new Configuration(),args[0], args[1]);

        System.out.println("****************Started Executing distanceTimeFraudJob ================");
        distanceTimeFraudJob.submit();
        if(distanceTimeFraudJob.waitForCompletion(true))
        {
            System.out.println("=================Completed DistanceTimeFraudJob================= ");
            System.out.println("=================Started Executing spendingFraudJob ================");
            spendingFraudJob.submit();
            if(spendingFraudJob.waitForCompletion(true))
            {
                System.out.println("=================Completed spendingFraudJob================= ");
                System.out.println("=================Started locationFraudJob================= ");
                locationFraudJob.submit();
                if(locationFraudJob.waitForCompletion(true))
                {
                    System.out.println("=================Completed locationFraudJob=================");
                }
            }
        }
    }

Ваш ответ о том, как присоединиться к этим заданиям с точки зрения исполнения. Первоначальный вопрос был о лучших структурах данных. Так что ваш ответ не имеет отношения к этому конкретному вопросу.
Niels Basjes

2

Новый класс org.apache.hadoop.mapreduce.lib.chain.ChainMapper помогает в этом сценарии


1
Ответ хороший, но вы должны добавить более подробную информацию о том, что он делает, или, по крайней мере, ссылку на справочник по API, чтобы люди могли проголосовать за него
Джереми Хайек,

ChainMapper и ChainReducer используются для того, чтобы иметь 1 или более сопоставителей до Reduce и 0 или более сопоставлений после Reduce, spec. (Картограф +) Уменьшить (Картограф *). Поправьте меня, если я ошибаюсь, очевидно, но я не думаю, что этот подход обеспечивает последовательное связывание заданий в соответствии с просьбой OP.
oczkoisse

1

Хотя существуют сложные серверные механизмы рабочего процесса Hadoop, например oozie, у меня есть простая библиотека Java, которая позволяет выполнять несколько заданий Hadoop в качестве рабочего процесса. Конфигурация задания и рабочий процесс, определяющий зависимость между заданиями, настраиваются в файле JSON. Все настраивается извне и не требует каких-либо изменений в существующей реализации сокращения карты, чтобы стать частью рабочего процесса.

Подробности можно найти здесь. Исходный код и jar доступны в github.

http://pkghosh.wordpress.com/2011/05/22/hadoop-orchestration/

Pranab


1

Я думаю, что oozie помогает последующим заданиям получать данные непосредственно от предыдущей работы. Это позволяет избежать операций ввода-вывода, выполняемых с помощью управления заданиями.


1

Если вы хотите программно объединить свои задания, вы захотите использовать JobControl. Использование довольно простое:

JobControl jobControl = new JobControl(name);

После этого вы добавляете экземпляры ControlledJob. ControlledJob определяет задание со своими зависимостями, таким образом, автоматически подключает входные и выходные данные для соответствия «цепочке» заданий.

    jobControl.add(new ControlledJob(job, Arrays.asList(controlledjob1, controlledjob2));

    jobControl.run();

запускает цепочку. Вы захотите разместить это в специальной ветке. Это позволяет проверять статус вашей цепочки во время ее работы:

    while (!jobControl.allFinished()) {
        System.out.println("Jobs in waiting state: " + jobControl.getWaitingJobList().size());
        System.out.println("Jobs in ready state: " + jobControl.getReadyJobsList().size());
        System.out.println("Jobs in running state: " + jobControl.getRunningJobList().size());
        List<ControlledJob> successfulJobList = jobControl.getSuccessfulJobList();
        System.out.println("Jobs in success state: " + successfulJobList.size());
        List<ControlledJob> failedJobList = jobControl.getFailedJobList();
        System.out.println("Jobs in failed state: " + failedJobList.size());
    }

0

Как вы упомянули в своем требовании, что хотите, чтобы o / p MRJob1 было i / p MRJob2 и так далее, вы можете рассмотреть возможность использования рабочего процесса oozie для этого варианта использования. Также вы можете рассмотреть возможность записи промежуточных данных в HDFS, поскольку они будут использоваться в следующем MRJob. А после завершения работы вы можете очистить промежуточные данные.

<start to="mr-action1"/>
<action name="mr-action1">
   <!-- action for MRJob1-->
   <!-- set output path = /tmp/intermediate/mr1-->
    <ok to="end"/>
    <error to="end"/>
</action>

<action name="mr-action2">
   <!-- action for MRJob2-->
   <!-- set input path = /tmp/intermediate/mr1-->
    <ok to="end"/>
    <error to="end"/>
</action>

<action name="success">
        <!-- action for success-->
    <ok to="end"/>
    <error to="end"/>
</action>

<action name="fail">
        <!-- action for fail-->
    <ok to="end"/>
    <error to="end"/>
</action>

<end name="end"/>

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.