1. 程式人生 > >關於Hadoop學習的感悟(一)

關於Hadoop學習的感悟(一)

Hadoop學習感悟(一)

Hadoop的安裝

Hadoop在Linux下安裝相對較為簡單。具體可參考網上的安裝教程,也可直接到Apache網站上找到安裝指南。這裡需要注意的是找對Hadoop的版本和對應的安裝教程

例如Hadoop 2.5.2的Apache文件可以直接到地址找到安裝教程(個人感覺Apache的安裝教程較為清楚,所以如果有條件還是按照官網的來吧)。

吐槽-,-

可以說對Map-Reduce的理解是我花費時間較長的一部分。主要是學習過程中遇到的各種坑(自己挖的和別人挖的)。先說說客觀原因。當初手賤買了本《Hadoop應用開發技術詳解》@劉剛 。個人不推薦這本書,整本書的章節安排、語言、程式碼都比較差勁。可以說是作者本人從開始學習計算機到現在讀到的最差的書(沒有之一)。然後就是自己挖的坑,剛開始跟著書寫的WordCount

小程式,由於自己改了一行程式碼,導致結果全是1。然後是對Map-Reduce過程中的Shuffle過程理解,翻了好幾天的部落格都沒找到我想要的,這些在後面會詳細說。

WordCount小程式

WordCount在Map-Reduce框架裡應該算是我們剛學習語言的Hello ,world了吧。本人寫的WordCount程式分為三個類,分別是MapperReducerMain , 程式碼如下:

WordMapper

public class WordMapper extends Mapper<LongWritable, Text, Text, IntWritable
> {
private static IntWritable one = new IntWritable(1); Text word = new Text(); public void map(LongWritable ikey, Text ivalue, Context context) throws IOException, InterruptedException { StringTokenizer itr = new StringTokenizer(ivalue.toString()); while(itr.hasMoreTokens()){ word.set(itr.nextToken()); context.write(word, one); } } }

WordReducer

public class WordReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
    private IntWritable result = new IntWritable();
    public void reduce(Text _key, Iterable<IntWritable> values, Context context)
            throws IOException, InterruptedException {
        // process values
        int sum = 0;
        for (IntWritable val : values) {
            sum = sum + 1; //sum += val.get()
        }
        result.set(sum);
        context.write(_key, result);
    }

}

Main

public class Main {

    public static void main(String[] args) {
        Configuration conf = new Configuration();
        try {
            GenericOptionsParser parser = new GenericOptionsParser(conf, args);
            args = parser.getRemainingArgs();
            Job job = Job.getInstance(conf , "wordcount");

            job.setJarByClass(Main.class);
            job.setMapperClass(WordMapper.class);
            job.setReducerClass(WordReducer.class);
            job.setCombinerClass(WordReducer.class);
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(IntWritable.class);
            FileInputFormat.addInputPath(job, new Path(args[0]));
            FileOutputFormat.setOutputPath(job, new Path(args[1]));
            job.submit();
            System.exit(job.waitForCompletion(true) ? 0 : 1);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }
}

在Reducer的實現裡,我的想法是,既然map任務的輸出是每個key的value都是1,那直接加1val.get()的結果是一樣的。所以自己改為了sum+=1。。。最後的結果是每個key的統計都是1。

分析

由於是剛剛接觸這個框架(之前只是瞭解大體的流程),所以開始了漫無目的的查詢(這個問題真心不知道怎麼在搜尋引擎上搜)。後來自己檢視hadoop的userlog,看到輸出分為了三個部分,結合對Map-Reduce的一點理解,分別是Map,Combine,Reduce三個過程的輸出。後來把Main類中main方法裡的setCombinerClass這句註釋掉後,一切正常。

由Combine過程引發的思考

由於看到了Combine過程,自然要好好了解下,在看了許多部落格後,總結如下:

Map過程會將臨時結果寫到記憶體緩衝區中,當記憶體緩衝區佔用達到一定百分比(預設80%,後面以緩衝區100MB為例)後,會啟動溢寫程序spill到磁碟中去(此階段會對key進行排序),將這80%寫入到磁碟檔案中去,剩餘的20MB可以繼續寫。

如果map的輸出較多,會產生較多的臨時檔案,最終會Merge成一個檔案(網上的說法)。

這裡我的疑問是:每個map的輸出都是不同的,上面說的Merge成一個檔案是所有map的輸出都Merge到一起還是每個map的輸出merge到一起,然後由Recuder去取?