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去取?