WordCount——MapReduce 实例入门

本文我们从一个简单的实例出发,统计文本中不同单词出现的次数,来讲述 MapReduce 的执行流程。

考虑如下的文本信息(文件名为hello):

hello you
hello me

MapReduce 工作流程

  • (1) [K1, V1]:将输入文本的每一行,解析成一个 key、value 对

    键:当前文本行的首地址,则第一行的首地址为0,则第二行的首地址为10(第一行的换行也站一个字节)。

    值:当前文本行文本 内容。

    第一步解析为:[0, hello you]、[10, hello me]

    每一个键值对调用一次 Map 函数,则就会调用两次 Map 函数

  • (2)Map:[K1, V1] ⇒ [K2, V2]

    Map 函数接受的是每一行的文本信息,它无法获取所有行的内容,自然它处理的也是一个单独的文本行的内容,对本例而言也即统计当前行中单词出现的次数;

    public void map(K, V, ctx){
        String[] splited = v.split("\t");
        for (String word: splited){
            ctx.write(<word, 1>);
                        // 向上下文中写数据
        }
    }

    本例而言:<hello, 1>, <you, 1>, <hello, 1>, <me, 1>

  • (3)默认为1个分区

  • (4)先排序再分组,将相同 key (实现了 Comparable 接口的 compareTo 方法)的不同 values 置于一个集合中

    先排序:<hello, 1>, <hello, 1>, <me, 1>, <you, 1>
    分组:<hello, {1, 1}>, <me, 1>, <you, 1>

  • (5)规约

  • (6)拷贝到 Reducer 所在的节点

    属于框架的工作;

  • (7)Reduce

    本例而言,reduce被调用三次;

    public void reduce(K, Vs, ctx){
        ctx.write(K, Vs.size());        
    }
  • (8)保存结果

    <hello, 2>, <me, 1>, <you, 1>

如何以 MapReduce 的思维处理业务

  • (1)在MapReduce中流转的是键值对

    input ⇒ <K1, V1> ⇒ <K2, V2> ⇒ <K2, V2s> ⇒ <K3, V3>

  • (2)客户输入的文本信息提供了<K1, V1>,客户的要求给定了<K3, V3><K2、V2>在 Map 产生,在 Reduce 步被消灭)

  • (3)使用 MapReduce 的核心就在于确定<K2, V2>,如何确定K2, V2呢:

    • (1)分组:把相同 key 的 values 放在一起
    • (2)Reduce 函数

代码实现

package mr;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.HashPartitioner;

public class WordCount {

    static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{
        @Override
        protected void map(LongWritable k1, Text v1,
                Mapper<LongWritable, Text, Text, LongWritable>.Context ctx)
                throws IOException, InterruptedException {
            String[] splits = v1.toString().split(" ");
            for (String word: splits)
                ctx.write(new Text(word),  new LongWritable(1L));
        }
    } 

    static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable>{
        @Override
        protected void reduce(Text k2, Iterable<LongWritable> v2s,
                Reducer<Text, LongWritable, Text, LongWritable>.Context ctx)
                throws IOException, InterruptedException {
            long cnt = 0L;
            for (LongWritable v2 : v2s) {
                cnt += v2.get();
            }
            ctx.write(k2, new LongWritable(cnt));
        }
    }

    private static final String INPUT_PATH = "hdfs://hadoop0:9000/hello";
    private static final String OUTPUI_PATH = "hdfs://hadoop0:9000/hello_res";

    public static void main(String[] args) throws Exception {

        Configuration conf = new Configuration();
        Job job = new Job(conf, WordCount.class.getSimpleName());

        // step 1: 输入   ==> <K1, V1>
        // 向任务传递输入文件
        FileInputFormat.setInputPaths(job, INPUT_PATH);
        // 指定对输入文件进行格式化处理的类
        job.setInputFormatClass(TextInputFormat.class);
                                    // 注意:FileInputFormat TextInputFormat 包的选择
                                    // 步骤可省
        // step 2: <K1, V1> ==> <K2, V2>
        job.setMapperClass(MyMapper.class);
        // 指定map输出的<K, V>类型,如果<K3, V3> 与 <K2, V2>的类型一致,以下的两个设置也是可省的
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(LongWritable.class);


        // step 3: 分区
        job.setPartitionerClass(HashPartitioner.class);
        job.setNumReduceTasks(1);
                                    // 以上两个设置均可省略
                                    // 也即以上两个操作都是默认操作
        // step 4: 排序、分组

        // step 5:规约

        // step 2.1
        // step 2.2:指定自定义Reducer类
        job.setReducerClass(MyReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);


        // step 2.3: 指定输出路径
        FileOutputFormat.setOutputPath(job, new Path(OUTPUI_PATH));
        // 指定对输出结果进行格式化处理的类
        job.setOutputFormatClass(TextOutputFormat.class);
                                    // 此步可省
        // 将作业提交给 JobTracker
        job.waitForCompletion(true);
    }
}

在启动hadoop的之后,运行该程序时,可能会报权限异常,将 FileUtil 解压拷贝到当前目录再次运行即顺利执行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五道口纳什

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值