Об’єднання декількох завдань 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 допоможе вам у цьому: Chaining Jobs

Ви використовуєте JobClient.runJob(). Шлях виведення даних з першого завдання стає вхідним шляхом до другого завдання. Вони повинні бути передані як аргументи вашим завданням з відповідним кодом для їх розбору та встановлення параметрів для завдання.

Я думаю, що описаний вище метод може бути таким, як це зробив тепер уже старший API, але все одно він повинен працювати. У новому API mapreduce буде подібний метод, але я не впевнений, що це таке.

Що стосується видалення проміжних даних після закінчення завдання, ви можете зробити це у своєму коді. Як я це робив раніше, використовую щось на кшталт:

FileSystem.delete(Path f, boolean recursive);

Де шлях - розташування даних на HDFS. Вам потрібно переконатися, що ви видаляєте ці дані лише після того, як жодна інша робота цього не потребує.


3
Дякуємо за посилання на підручник Yahoo. Робота ланцюга - це дійсно те, що ви хочете, якщо обидва будуть одночасно. Що я шукав - це простий спосіб зробити, якщо ви хочете мати можливість запускати їх окремо. У згаданому підручнику я знайшов SequenceFileOutputFormat "Записує бінарні файли, придатні для читання на наступних завданнях MapReduce", і відповідний SequenceFileInputFormat, який робить це все дуже просто. Дякую.
Нільс Базес

20

Є багато способів зробити це.

(1) Каскадні завдання

Створіть об'єкт JobConf "job1" для першого завдання та встановіть усі параметри за допомогою "input" як вхідного довідника, а "temp" як вихідного каталогу. Виконайте це завдання:

JobClient.run(job1).

Безпосередньо під ним створіть об'єкт JobConf "job2" для другого завдання та встановіть усі параметри з "temp" як вхідний довідник, а "output" - як вихідний каталог. Виконайте це завдання:

JobClient.run(job2).

(2) Створіть два об'єкти JobConf і встановіть усі параметри в них так само, як (1), за винятком того, що ви не використовуєте JobClient.run.

Потім створіть два об'єкти Job з завданнями confs як параметри:

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

Використовуючи об'єкт taskControl, ви вказуєте залежності роботи та виконуєте завдання:

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 вручну, а розробляти свою програму за допомогою каскадного API. Потім JobConf та ланцюжок робочих місць обробляються внутрішньо за допомогою планувальника каскаду та класів Flow.

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


6

Я зробив ланцюжок роботи, використовуючи об'єкти JobConf одна за одною. Я взяв приклад WordCount для прив’язки завдань. Одне завдання визначає, скільки разів слово повторюється в даному виході. Друге завдання приймає перше завдання як вхід і фіксує загальні слова на даному вході. Нижче наведено код, який потрібно розмістити в класі 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);

Команда для виконання цих завдань:

бін / хадоп 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
Це було зроблено , щоб отримати ключі упорядковано ще існують і інші способи , такі як використання TreeMap
Але я хочу звернути вашу увагу на те , як Джобс був прикутий! !
Дякую




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, щоб люди могли голосувати під голосом
Jeremy Hajek

ChainMapper і ChainReducer використовуються для того, щоб мати 1 або більше картографів перед зменшенням і 0 або більше картографів після зменшення, специфікація. (Mapper +) Зменшити (Mapper *). Виправте мене, якщо я помиляюся, очевидно, але я не думаю, що такий підхід виконує послідовне пов'язування робочих місць, як просив ОП.
oczkoisse

1

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

Деталі можна знайти тут. Вихідний код і банку доступні в github.

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

Пранаб


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.