Hadoop三大组件之一:MapReduce

概述

  1. MapReduce是Doug根据的Google的<The Google MapReduce>来仿照实现的

  2. MapReduce将整个计算过程拆分为了两个大阶段:Map(映射)阶段和Reduce(规约)阶段

入门案例

  1. 案例:统计这个文件中每一个字符出现的次数

  2. MapReduce刚开始的时候,会先对要处理的文件进行切片(Split)。

    1. Split是逻辑切分,是在划分任务量

    2. Block是物理切分,是在确定存储单位

  3. 实际过程中,考虑到数据要跨节点传输,为了减少跨节点的数量,所以Split=Block/n。在MapReduce中,默认Split和Block等大

  4. Mapper类

    package com.lcs.charcount;
    ​
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.LongWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Mapper;
    ​
    import java.io.IOException;
    ​
    // Mapper
    // 在MapReduce中,要求传输的数据能够被序列化
    // 而MapReduce使用的不是Java的原生序列化机制,而是采用了新的序列化机制
    // KEYIN - 输入的键的类型,是这一行的字节偏移量
    // VALUEIN - 输入的值的类型,是这一行数据
    // KEYOUT - 输出的键的类型。当前案例中,输出的键是字符
    // VALUEOUT - 输出的值的类型。当前案例中,输出的值是次数
    public class CharCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
    ​
        private final IntWritable once =  new IntWritable(1);
    ​
        // MapTask的处理逻辑是放在map方法中
        // 切片中的每一行都会调用一次map方法进行处理
        // key:键。每一行的字节偏移量
        // value:值。这一行数据
        // context:环境参数
        @Override
        protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException {
            // 将这一行中的字符拆分出来
            char[] cs = value.toString().toCharArray();
            //
            for (char c : cs) {
                context.write(new Text(String.valueOf(c)), once);
            }
        }
    }

  5. Reducer类

    package com.lcs.charcount;
    ​
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Reducer;
    ​
    import java.io.IOException;
    ​
    // KEYIN,VALUEIN - 输入的键和值。Reducer的数据是Mapper产生的,那么就意味着Mapper的输出类型就是Reducer的输入类型
    // KEYOUT,VALUEOUT - 输出的键和值。当前案例中,最终输出的字符和对应的次数
    public class CharCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
    ​
        // key:键。当前案例中,键是字符
        // values:值。Reduce开始的时候会将相同的键对应的值放到一起,这个过程称之为分组。分组结束之后,会将值放入迭代器中,每一个键调用一次reduce方法
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
            // key:h
            // values:1,1,1,1,1,1....
            // 定义变量记录和
            int sum = 0;
            // 遍历values
            for (IntWritable value : values) {
                sum += value.get();
            }
            context.write(key, new IntWritable(sum));
    ​
        }
    }

  6. Driver类(入口类)

    package com.lcs.charcount;
    ​
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.Path;
    import org.apache.hadoop.io.IntWritable;
    import org.apache.hadoop.io.Text;
    import org.apache.hadoop.mapreduce.Job;
    import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
    import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
    ​
    import java.io.IOException;
    ​
    public class CharCountDriver {
    ​
        public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
    ​
            // 环境参数
            Configuration conf = new Configuration();
            // 申请任务
            Job job = Job.getInstance(conf);
            // 指定入口类
            job.setJarByClass(CharCountDriver.class);
            // 指定Mapper类
            job.setMapperClass(CharCountMapper.class);
            // 指定Reducer类
            job.setReducerClass(CharCountReducer.class);
            // 指定Mapper的输出类型
            job.setMapOutputKeyClass(Text.class);
            job.setMapOutputValueClass(IntWritable.class);
            // 指定Reducer的输出类型
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(IntWritable.class);
            // 指定输入路径
            FileInputFormat.addInputPath(job, new Path("hdfs://10.16.3.181:9000/txt/characters.txt"));
            // 指定输出路径,要求输出路径必须不存在
            FileOutputFormat.setOutputPath(job, new Path("hdfs://10.16.3.181:9000/result/char_count"));
            // 提交任务
            job.waitForCompletion(true);
        }
    }

    组件

    序列化 - Writable

  7. 统计每一个人花费的上行流量、下行流量以及总流量(文件:flow.txt)

  8. 在MapReduce中,各个节点之间基本上都是通过RPC的方式来进行调用,也因此要求传输的数据必须被序列化

  9. Hadoop并没有使用Java的原生序列化机制,底层默认采用了AVRO来进行的序列化。在AVRO的基础上,MapReduce进行了封装,从而简化了序列化操作 - 让需要被序列化的对象对应的类实现接口Writable,覆盖其中的writereadFields方法即可

  10. 由于AVRO的限制,所以要求被序列化的类中必须有无参构造,同时不允许属性值为null

  11. MapReduce针对常用的类都提供了序列化形式

    Java类序列化类
    ByteByteWritable
    ShortShortWritable
    IntegerIntWritable
    LongLongWritable
    FloatFloatWritable
    DoubleDoubleWritable
    BooleanBooleanWritable
    StringText
    NullNullWritable
    ArrayArrayWritable
    MapMapWritable

    分区 - Partitioner

  12. 按地区,统计每一个人花费的总流量(文件:flow.txt)

  13. Partitioner(分区)的作用是对数据进行分类

  14. 默认情况下,MapReduce中,只有一个1个分区,所以也只有1个ReduceTask。如果进行了分类,那么就需要指定多个ReduceTask。每一个分区都需要对应一个ReduceTask,每一个ReduceTask都会产生一个结果文件 - 分区的数量决定了ReduceTask的数量,ReduceTask的数量决定了结果文件的数量

  15. 在MapReduce中,会对分区进行编号,编号是从0开始的

  16. 排序 - Comparable

  17. 在MapReduce中,无论逻辑是否需要,默认会对MapTask的输出的键进行排序,也因此要求放在Mapper输出的键对应的类必须实现Comparable接口。考虑到还要进行序列化,需要实现WritableComparable接口

  18. 如果需要指定自己的排序规则,那么需要定义类实现WritableComparable接口

  19. 案例:先按照月份升序排序,如果是同一个月,那么按照利润降序排序(文件:profit.txt)

  20. 合并 - Combiner

  21. Combiner是在不改变结果的前提下,减少ReduceTask的计算条数

  22. 如果需要使用Combiner,只需要在入口类中添加

    job.setCombinerClass(XXXReducer.class);
  23. 可以传递结果的运算,可以使用Combiner,例如求和、求积、去重、取最值等;不能传递结果的运算,不能使用Combiner,例如求平均值等

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值