關於Hadoop學習的感悟(一)
Hadoop學習感悟(一)
Hadoop的安裝
Hadoop在Linux下安裝相對較為簡單。具體可參考網上的安裝教程,也可直接到Apache網站上找到安裝指南。這裡需要注意的是找對Hadoop的版本和對應的安裝教程
例如Hadoop 2.5.2的Apache文件可以直接到地址找到安裝教程(個人感覺Apache的安裝教程較為清楚,所以如果有條件還是按照官網的來吧)。
吐槽-,-
可以說對Map-Reduce的理解是我花費時間較長的一部分。主要是學習過程中遇到的各種坑(自己挖的和別人挖的)。先說說客觀原因。當初手賤買了本《Hadoop應用開發技術詳解》@劉剛 。個人不推薦這本書,整本書的章節安排、語言、程式碼都比較差勁。可以說是作者本人從開始學習計算機到現在讀到的最差的書(沒有之一)。然後就是自己挖的坑,剛開始跟著書寫的WordCount
WordCount小程式
WordCount在Map-Reduce框架裡應該算是我們剛學習語言的Hello ,world了吧。本人寫的WordCount程式分為三個類,分別是Mapper , Reducer , Main , 程式碼如下:
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,那直接加1與val.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去取?