Hadoopでの複数のMapReduceジョブのチェーン


124

MapReduceを適用する多くの実際の状況では、最終的なアルゴリズムは最終的にいくつかのMapReduceステップになります。

つまり、Map1、Reduce1、Map2、Reduce2などです。

したがって、次のマップの入力として必要な最後の削減からの出力があります。

中間データは、パイプラインが正常に完了した後に(一般的に)保持したくないものです。また、この中間データは一般に一部のデータ構造(「マップ」や「セット」など)であるため、これらのキーと値のペアの書き込みと読み取りにあまり労力をかけたくありません。

Hadoopでそれを行うための推奨される方法は何ですか?

後のクリーンアップを含め、この中間データを正しい方法で処理する方法を示す(簡単な)例はありますか?


2
どのmapreduceフレームワークを使用していますか?
skaffman 2010年

1
Hadoopについて話していることを明確にするために質問を編集しました。
Niels Basjes

私はこのために豚の宝石をお勧めします:github.com/Ganglion/swineherd best、Tobias
Tobias

回答:


57

私はYahooの開発者向けネットワーク上でこのチュートリアルはこれであなたを助けると思う:チェーンジョブズ

を使用しJobClient.runJob()ます。最初のジョブからのデータの出力パスは、2番目のジョブへの入力パスになります。これらは、それらを解析し、ジョブのパラメーターを設定するための適切なコードを使用して、ジョブの引数として渡す必要があります。

しかし、上記の方法は、今では古いmapred APIが行った方法である可能性があると思いますが、それでも機能するはずです。新しいmapreduce APIにも同様のメソッドがありますが、それが何かはわかりません。

ジョブの終了後に中間データを削除する限り、コードでこれを行うことができます。私が以前にやった方法は、次のようなものを使用しています:

FileSystem.delete(Path f, boolean recursive);

パスは、HDFS上のデータの場所です。他のジョブが必要としない場合にのみ、このデータを削除する必要があります。


3
Yahooチュートリアルへのリンクをありがとう。2つのジョブが同じ実行である場合、チェーンジョブは実際に必要なものです。私が探していたのは、それらを個別に実行できるようにしたい場合の簡単な方法です。前述のチュートリアルで、SequenceFileOutputFormat "後続のMapReduceジョブへの読み取りに適したバイナリファイルを書き込みます"と、すべてを非常に簡単にする一致するSequenceFileInputFormatを見つけました。ありがとう。
Niels Basjes、

20

あなたがそれを行うことができる多くの方法があります。

(1)カスケードジョブ

最初のジョブのJobConfオブジェクト「job1」を作成し、「input」を入力ディレクトリ、「temp」を出力ディレクトリとしてすべてのパラメータを設定します。このジョブを実行します。

JobClient.run(job1).

そのすぐ下に、2番目のジョブのJobConfオブジェクト「job2」を作成し、すべてのパラメーターを「temp」を入力ディレクトリ、「output」を出力ディレクトリとして設定します。このジョブを実行します。

JobClient.run(job2).

(2) 2つのJobConfオブジェクトを作成し、JobClient.runを使用しないことを除いて、(1)と同じようにすべてのパラメーターを設定します。

次に、jobconfをパラメータとして2つのJobオブジェクトを作成します。

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 *。Hadoopバージョン0.19以降に付属するChainMapperおよびChainReducerクラスを使用できます。


7

これを行うには実際にいくつかの方法があります。2つに焦点を当てます。

1つは、Riffle(http://github.com/cwensel/riffle)を介して、依存するものを識別し、依存関係(トポロジー)の順序でそれらを「実行」するための注釈ライブラリです。

または、Cascading(http://www.cascading.org/)でCascade(およびMapReduceFlow)を使用できます。将来のバージョンではRiffleアノテーションがサポートされる予定ですが、未加工のMR JobConfジョブでうまく機能します。

これのバリエーションは、MRジョブを手動で管理するのではなく、Cascading APIを使用してアプリケーションを開発することです。次に、JobConfおよびジョブチェーンは、Cascading plannerおよびFlowクラスを介して内部的に処理されます。

このようにして、Hadoopジョブの管理のメカニズムなどではなく、問題に集中して時間を費やします。さまざまな言語(clojureやjrubyなど)を上に重ねて、開発とアプリケーションをさらに簡素化することもできます。http://www.cascading.org/modules.html


6

JobConfオブジェクトを使用してジョブチェーンを次々に実行しました。私は、ジョブを連鎖させるためにWordCountの例を取り上げました。1つのジョブは、与えられた出力で単語が何回繰り返されたかを計算します。2番目のジョブは、最初のジョブの出力を入力として受け取り、指定された入力の合計単語数を計算します。以下は、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);
      }

}

SEQUENCEは、IS

JOB1)MAP-> REDUCE->(JOB2)MAP
これは、キーを取得するために行われていたが、このようなツリーマップを使用するなど、より多くの方法があり、まだソート
しかし、私はジョブズが連鎖してきた道にあなたの注意を集中したいです! !
ありがとうございました




3

waitForCompletion(true)Job のメソッドを利用して、ジョブ間の依存関係を定義できます。

私のシナリオでは、相互に依存する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 2013年

2

新しいクラスorg.apache.hadoop.mapreduce.lib.chain.ChainMapperはこのシナリオに役立ちます


1
回答は良いですが、それが何をするか、または少なくともAPIリファレンスへのリンクを追加して、人々が賛成投票できるようにする必要があります
Jeremy Hajek

ChainMapperとChainReducerは、Reduce仕様の前に1つ以上のマッパーを、Reduce仕様の後に0以上のマッパーを使用するために使用されます。(Mapper +)削減(Mapper *)。私が明らかに間違っている場合は修正してください。ただし、このアプローチでは、OPが要求したとおりにジョブを連続的に連鎖させることができないと思います。
oczkoisse 2017

1

複雑なサーバーベースのHadoopワークフローエンジン(oozieなど)がありますが、ワークフローとして複数のHadoopジョブの実行を可能にする単純なJavaライブラリがあります。ジョブ構成とジョブ間の依存関係を定義するワークフローは、JSONファイルで構成されます。すべては外部から構成可能であり、既存のマップを変更する必要がなく、ワークフローの一部として実装を削減できます。

詳細はこちらです。ソースコードとjarはgithubで入手できます。

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

プラナブ


1

oozieは、後続のジョブが前のジョブから直接入力を受け取るのに役立つと思います。これにより、ジョブ制御で実行されるI / O操作が回避されます。


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

MRJob1のo / pをMRJob2のi / pにしたいという要件で述べたように、このユースケースではoozieワークフローの使用を検討できます。また、次のMRJobで使用されるため、中間データをHDFSに書き込むことも検討してください。そして、ジョブが完了した後、中間データをクリーンアップできます。

<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"/>

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.