การเชื่อมโยงงาน MapReduce หลายงานใน Hadoop


124

ในสถานการณ์จริงหลายอย่างที่คุณใช้ MapReduce อัลกอริทึมสุดท้ายจะจบลงด้วยขั้นตอน MapReduce หลายขั้นตอน

เช่น Map1, Reduce1, Map2, Reduce2 และอื่น ๆ

ดังนั้นคุณจึงมีผลลัพธ์จากการลดครั้งสุดท้ายที่จำเป็นเป็นอินพุตสำหรับแผนที่ถัดไป

ข้อมูลระดับกลางเป็นสิ่งที่คุณ (โดยทั่วไป) ไม่ต้องการเก็บไว้เมื่อดำเนินการไปป์ไลน์สำเร็จแล้ว เนื่องจากข้อมูลระดับกลางนี้โดยทั่วไปแล้วโครงสร้างข้อมูลบางส่วน (เช่น 'แผนที่' หรือ 'ชุด') คุณจึงไม่ต้องการใช้ความพยายามมากเกินไปในการเขียนและอ่านคู่คีย์ - ค่าเหล่านี้

วิธีที่แนะนำใน Hadoop คืออะไร?

มีตัวอย่าง (ง่ายๆ) ที่แสดงวิธีจัดการข้อมูลระดับกลางนี้ด้วยวิธีที่ถูกต้องรวมถึงการล้างข้อมูลในภายหลังหรือไม่?


2
ใช้ mapreduce framework ตัวไหน
skaffman

1
ฉันแก้ไขคำถามเพื่อชี้แจงว่าฉันกำลังพูดถึง Hadoop
Niels Basjes

ฉันขอแนะนำอัญมณีสุกรสำหรับสิ่งนี้: github.com/Ganglion/swineherdดีที่สุด Tobias
Tobias

คำตอบ:


57

ฉันคิดว่าบทช่วยสอนเกี่ยวกับเครือข่ายนักพัฒนาของ Yahoo จะช่วยคุณได้ในเรื่องนี้: Chaining Jobs

คุณใช้ไฟล์JobClient.runJob(). พา ธ เอาต์พุตของข้อมูลจากงานแรกกลายเป็นพา ธ อินพุตไปยังงานที่สองของคุณ สิ่งเหล่านี้จะต้องส่งต่อเป็นอาร์กิวเมนต์ไปยังงานของคุณด้วยรหัสที่เหมาะสมเพื่อแยกวิเคราะห์และตั้งค่าพารามิเตอร์สำหรับงาน

ฉันคิดว่าวิธีการข้างต้นอาจเป็นวิธีที่ mapred API รุ่นเก่าทำอยู่ในขณะนี้ แต่ก็ยังควรใช้งานได้ จะมีวิธีการที่คล้ายกันใน mapreduce API ใหม่ แต่ฉันไม่แน่ใจว่ามันคืออะไร

เท่าที่การลบข้อมูลระดับกลางหลังจากงานเสร็จสิ้นคุณสามารถทำได้ในโค้ดของคุณ วิธีที่ฉันเคยทำมาก่อนคือการใช้สิ่งต่างๆเช่น:

FileSystem.delete(Path f, boolean recursive);

โดยที่พา ธ คือตำแหน่งบน HDFS ของข้อมูล คุณต้องแน่ใจว่าคุณลบข้อมูลนี้เมื่อไม่มีงานอื่นต้องการ


3
ขอบคุณสำหรับลิงก์ไปยังบทช่วยสอน Yahoo Chaining Jobs เป็นสิ่งที่คุณต้องการหากทั้งสองอยู่ในระยะเดียวกัน สิ่งที่ฉันกำลังมองหาคือวิธีง่ายๆที่จะทำหากคุณต้องการให้สามารถเรียกใช้แยกกันได้ ในบทช่วยสอนที่กล่าวถึงฉันพบ SequenceFileOutputFormat "เขียนไฟล์ไบนารีที่เหมาะสมสำหรับการอ่านในงาน MapReduce ที่ตามมา" และ SequenceFileInputFormat ที่ตรงกันซึ่งทำให้ทุกอย่างง่ายมาก ขอบคุณ
Niels Basjes

20

มีหลายวิธีที่คุณสามารถทำได้

(1)งานเรียงซ้อน

สร้างอ็อบเจ็กต์ JobConf "job1" สำหรับงานแรกและตั้งค่าพารามิเตอร์ทั้งหมดด้วย "input" เป็น inputdirectory และ "temp" เป็นไดเร็กทอรีเอาต์พุต ดำเนินการงานนี้:

JobClient.run(job1).

ด้านล่างสร้างอ็อบเจ็กต์ JobConf "job2" สำหรับงานที่สองและตั้งค่าพารามิเตอร์ทั้งหมดโดยมี "temp" เป็น inputdirectory และ "output" เป็นไดเร็กทอรีเอาต์พุต ดำเนินการงานนี้:

JobClient.run(job2).

(2)สร้างอ็อบเจ็กต์ JobConf สองอ็อบเจ็กต์และตั้งค่าพารามิเตอร์ทั้งหมดในอ็อบเจ็กต์เช่นเดียวกับ(1)ยกเว้นว่าคุณไม่ได้ใช้ JobClient.run

จากนั้นสร้างวัตถุงานสองชิ้นโดยมี 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)หากคุณต้องการโครงสร้างคล้าย ๆ กับแผนที่ + | ลด | แผนที่ * คุณสามารถใช้คลาส 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 เพื่อผูกโยงงาน งานหนึ่งจะคำนวณว่ามีคำซ้ำกี่ครั้งในผลลัพธ์ที่กำหนด งานที่สองรับเอาต์พุตงานแรกเป็นอินพุตและคำนวณจำนวนคำทั้งหมดในอินพุตที่กำหนด ด้านล่างนี้คือรหัสที่ต้องใส่ในคลาส 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 chain ในลักษณะที่ระบุไว้ในรหัส

โปรดทราบ : มีการระบุเฉพาะรหัสไดรเวอร์เท่านั้น

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)วิธีการของงานเพื่อกำหนดการพึ่งพาระหว่างงาน

ในสถานการณ์ของฉันฉันมีงาน 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 *) แก้ไขฉันถ้าฉันผิดอย่างเห็นได้ชัด แต่ฉันไม่คิดว่าวิธีนี้จะบรรลุการผูกมัดงานตามลำดับตามที่ OP ขอ
oczkoisse

1

แม้ว่าจะมีเครื่องมือเวิร์กโฟลว์ Hadoop ที่ใช้เซิร์ฟเวอร์ที่ซับซ้อนเช่น oozie แต่ฉันก็มีไลบรารี java ที่เรียบง่ายที่ช่วยให้สามารถเรียกใช้งาน Hadoop หลาย ๆ งานเป็นเวิร์กโฟลว์ได้ คอนฟิกูเรชันงานและเวิร์กโฟลว์ที่กำหนดการพึ่งพาระหว่างงานถูกกำหนดคอนฟิกในไฟล์ JSON ทุกอย่างสามารถกำหนดค่าได้จากภายนอกและไม่ต้องการการเปลี่ยนแปลงใด ๆ ในการลดการใช้งานแผนที่ที่มีอยู่เพื่อเป็นส่วนหนึ่งของเวิร์กโฟลว์

สามารถดูรายละเอียดได้ที่นี่ ซอร์สโค้ดและ jar มีอยู่ใน github

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

มู


1

ฉันคิดว่า oozie ช่วยให้งานที่ตามมาได้รับอินพุตโดยตรงจากงานก่อนหน้า สิ่งนี้หลีกเลี่ยงการดำเนินการ I / o ที่ดำเนินการกับ jobcontrol


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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.