MapReduce

思想

MapReduce思想在生活中处处可见。或多或少都曾接触过这种思想。MapReduce的思想核心是分而治之,适用于大量复杂的任务处理场景(大规模数据处理场景)。

  • Map负责,即把复杂的任务分解为若干个简单的任务来并行处理。可以进行拆分的前提是这些小任务可以并行计算,彼此间几乎没有依赖关系。
  • Reduce负责,即对map阶段的结果进行全局汇总。

这两个阶段合起来正是MapReduce思想的体现。

还有一个比较形象的语言解释MapReduce:

我们要数图书馆中的所有书。你数1号书架,我数2号书架。这就是“Map”。我们人越多,数书就更快。

现在我们到一起,把所有人的统计数加在一起。这就是“Reduce”

 

图示

图解

Map阶段是一个整体,包括我们自定义的Mapper类完成的MapTask阶段和Hadoop框架为我们提供的Shuffle阶段的分区,排序,规约的默认处理(Shuffle阶段我们也可以自定义)

Reduce阶段也是一个整体,包括我们自定义的Reducer类完成的ReduceTask阶段和Hadoop框架为我们提供的Shuffle阶段的分组的默认处理(相同键的值放在同一个集合)

ReduceTask阶段输入的K2-V2是经过Shuffle分组(相同键的值放在同一个集合)之后的键值对, 由Hadoop框架默认提供

MapReduce也可以只有Map阶段没有Reduce阶段,这样的输出结果就是完成MapTask阶段和Shuffle阶段的分区,排序,规约的默认处理后的结果

一个DataNode数据块对应一个MapTask

多个DataNode数据块对应的MapTask并行处理

一个分区对应一个ReduceTask

多个分区对应的ReduceTask并行处理

 

案例代码-单词统计

 

WordCountMapper

package com.sz.mapreduce;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Counter;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

/**
 * 自定义map类
 *将k1 v1 转为k2 v2
 *继承Mapper类 重写map方法
 *
 */
public class WordCountMapper extends Mapper<LongWritable,Text,Text,LongWritable> {
    /**
     * 将k1 v1 转为k2 v2
     * k1   v1
     * 0    hello,world,hadoop
     * 24   hive,sqoop,flume,hello
     * -------------------------------
     * k2   v2
     * hello    1
     * world    1
     * --------------------------------
     * 思路:
     * 将v1以','分隔符进行分割,产生字符串数组
     *遍历字符串数组 统计个数
     * 字符串作为k2 个数作为v2
     *
     * @param key
     * @param value
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String s = value.toString();
        //将v1以','分隔符进行分割,产生字符串数组
        String[] split = s.split(",");
        //遍历字符串数组 统计个数
        for (String k2:split) {
            context.write(new Text(k2),new LongWritable(1));
        }
    }
}

WordCountReducer

package com.sz.mapreduce;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

/**
 * 自定义Reducer类
 * 将k2-v2 转为k3-v3
 * 继承Reduce类 重写reduce方法
 */
public class WordCountReducer extends Reducer<Text,LongWritable,Text,LongWritable>{
    /**
     * 自定义Reduce逻辑
     * 统计单词个数
     * 将shuffle之后的k2-v2 转为k3-v3
     * k2-v2
     * hello    <1,1,1>
     * world    <1,1>
     * ------------------------
     * k3-v3
     * hello    3
     * world    2
     * 转换思路
     * 键不变k2-k3
     * 遍历v2值 统计相加 结果作为新值v3
     * @param key
     * @param values
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
        //遍历v2值 统计相加 结果作为新值v3
        long v3 = 0;
        for (LongWritable v:values) {
            v3 +=v.get();
        }
        context.write(key,new LongWritable(v3));
    }
}

jobMain

package com.sz.mapreduce;


import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
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.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import java.io.File;

public class jobMain extends Configured implements Tool{
    public static void main(String args[]) throws Exception {
        //这里创建了一个configuration对象,好像并没有给他赋值初始化,空对象好像并没有什么用
        //其实Configuration有一个静态代码块(682行)在创建对象时会执行,可以加载很多配置文件文件完成初始化
        Configuration configuration = new Configuration();
        //启动job任务
        //set the configuration back, so that Tool can configure itself
        //这个方法会使用configuration对象参数配置tool对象的实现类 tool.setConf(conf);
        //这个方法会调用tool 对象的run()方法 这里的话就是tool对象的实现类jobMain重写的run()方法 return tool.run(toolArgs);
        //public static int run(Configuration conf, Tool tool, String[] args)
        int run = ToolRunner.run(configuration, new jobMain(), args);
        System.exit(run);
    }
    //该方法用于创建一个job任务
    @Override
    public int run(String[] args) throws Exception {
        //创建job任务对象
        Job job = Job.getInstance(super.getConf(), "WordCount");


        //配置job任务(八个步骤)
        //输入
        //指定文件的读取方式和路径
        job.setInputFormatClass(TextInputFormat.class);
//        Path input = new Path("hdfs://node01:8020/wordcount");
        Path input = new Path("file:///D:\\hadoop_test\\wordcount_input");
        TextInputFormat.addInputPath(job,input);

        //Map阶段
        //指定Map阶段的处理方式和数据类型
        job.setMapperClass(WordCountMapper.class);
        //设置Map阶段k2类型
        job.setMapOutputKeyClass(Text.class);
        //设置Map阶段v2类型
        job.setMapOutputValueClass(LongWritable.class);

        //shuffle阶段 采用默认方式
        //分区:
        //设置我们的分区类,以及我们的reducetask的个数,注意reduceTask的个数一定要与我们的分区数保持一致
//        job.setPartitionerClass(WordPartition.class);
//        job.setNumReduceTasks(3);

        //Reduce阶段
        //指定Reduce阶段的处理方式和数据类型
        job.setReducerClass(WordCountReducer.class);
        //设置Reduce阶段k3类型
        job.setOutputKeyClass(Text.class);
        //设置Reduce阶段v3类型
        job.setOutputValueClass(LongWritable.class);

        //输出
        //设置输出类型和路径
        job.setOutputFormatClass(TextOutputFormat.class);
//        Path path = new Path("hdfs://node01:8020/wordcount_out");
        Path path = new Path("file:///D:\\hadoop_test\\wordcount_output");
        TextOutputFormat.setOutputPath(job,path);
		
        //等待执行
        boolean b = job.waitForCompletion(true);
        //执行成功返回0
        return b?0:1;
    }
}

WordPartition

package com.sz.mapreduce;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;

/**
 * 分区类
 * 这里的输入类型就是Map阶段结束后的输出类型
 * k2   v2
 * hello    1
 * world    1
 */
public class WordPartition extends Partitioner<Text,LongWritable>{
    @Override
    public int getPartition(Text key, LongWritable value, int i) {
        if (key!=null) {
            int length = key.toString().length();
            if(length>5){
                return 0;
            }else if(length==5){
                return 1;
            }else{
                return 2;
            }
        }
        return 0;
    }
}

运行结果

源文件数据
hello,world,hadoop
hive,sqoop,flume,hello
kitty,tom,jerry,world
hadoop

K1(行偏移量) V1(Text)
0 hello,world,hadoop
19 hive,sqoop,flume,hello
42 kitty,tom,jerry,world
64 hadoop

MapTask(自定义)
K2    V2
hello	1
world	1
hadoop	1
hive	1
sqoop	1
flume	1
hello	1
kitty    1
tom     1
jerry	1
world	1
hadoop	1

排序
K2    V2
flume	1
hadoop	1
hadoop	1
hello	1
hello	1
hive	1
jerry	1
kitty	1
sqoop	1
tom     1
world	1
world	1


分组
K2    V2
flume	1
hadoop	<1,1>
hello	<1,1>
hive	1
jerry	1
kitty	1
sqoop	1
tom     1
world	<1,1>

ReduceTask(自定义)
不分区结果
K3    V3
flume	1
hadoop	2
hello	2
hive	1
jerry	1
kitty	1
sqoop	1
tom     1
world	2

按单词长度三分区分区结果
0分区
hadoop	2

1分区
flume	1
hello	2
jerry	1
kitty	1
sqoop	1
world	2

2分区
hive	1
tom     1

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值