MapReduce

一、Mapreduce的基础

1.1 为什么要学习Mapreduce

1. 单机资源受限,比如CPU,磁盘
2. 分布式计算的程序的复杂度特别高,难度大
​
mapreduce就是解决以上问题的:  
    1. 利用集群的所有cpu,所有内存,所有磁盘
    2. mapreduce就将公共的功能的开发封装成了框架,不需要开发人员操心,开发人员只需要关注具体的业务逻辑
​

1.2 Mapreduce的简介

1.2.1 简介

1. mapreduce是hadoop的三大重要模块之一
2. mapreduce是一个并发的计算和分析框架,用于计算和分析分布式文件系统上的大数据集。
3. 将计算划分为两个阶段:一个map(映射)阶段,一个reduce(归约)阶段
4. 该框架的开发灵感来源于google的《mapreduce》论文
5. 方便开发人员在不会分布式计算的情况下,开发和运行相关计算程序。

1.2.2 优缺点

1. 优点
    - 适合离线数据处理
    - mapreduce编程简单
    - 扩展性良好
    - 高容错性
2. 缺点
    - 不适合实时计算(实时计算:毫秒级别/秒级别,离线计算:秒级别以上)
    - 不适合流式计算(mapreduce处理的数据是静态的,不是流式的数据)
    - 不适合DAG(有向图)计算

1.3 Mapreduce的核心思想(重点)

简单的一句话概括:“移动计算而非移动数据”。
​
整理:
程序员将自己写好的业务逻辑代码和mr自带的一些组件打包成计算程序,移动到有数据存储的节点上,这样可以利用多节点的cpu的并发能力,提高计算效率(怎么提高的?一减少数据移动的开销,二利用了并发计算原理)
mapreduce是分为两个阶段,map阶段处理的是块文件(原始文件),计算后的结果存储本地磁盘,reduce阶段要跨节点fetch属于自己要处理的数据,计算后的结果存储到hdfs上(当然也可以存储到客户端所在的本地磁盘)

1.4 Mapreduce的阶段介绍(重点)

1.4.1 Map阶段

map阶段处理的是原始数据,也就是块文件(处理的是本存储节点上的数据)。会将处理的块文件,以切片的形式进行逻辑运算。通过映射关系进行一一映射。map阶段会有多个mapTask,这些任务并发运行,互不干扰
默认情况下,按行进行映射成键值对,
原始块文件
   |
   |
K1,V1(有N个kv对,K1是行偏移量,v1是行记录,也就是行内容)
   |
   |
  map方法
   |
   | 每一对k1v1都会调用一次map方法,在map方法里进行处理,形成K2V2 
   |
 K2,V2  (存储到本地磁盘)

1.4.2 Reduce阶段

reduce阶段处理的是map阶段计算出来的数据(临时数据),reduce阶段也会有多个reduceTask,并发运行,互不干扰。reduce处理的数据通常都是要跨节点fetch属于自己处理的数据。
​
fetch属于自己的一堆K2,v2,先形成<K2,<v2,v2,v2>>
         |
         |
       reduce方法
         |
         |  同一个k2调用一次reduce方法,在reduce方法里进行处理,形成K3,v3
         |
       K3,V3(存储到HDFS上)

1.5 Mapreduce的编程模型

1.5.1 自定义Mapper类型

1. 自定义类名,继承Mapper类型
2. 定义K1,V1,K2,V2的泛型
3. 重写map方法

1.5.2 自定义Reducer类型

1. 自定义类名,继承Reducer类型
2. 定义K2,V2,K3,V3的泛型
3. 重写reduce方法

1.5.3 自定义Driver类型

1. 获取job对象
2. 指定驱动类型
3. 指定Mapper类型和Reducer类型
4. 指定map阶段的K2,V2类型
5. 指定reduce阶段的K3,V3类型
6. 指定分区的数量.....
7. 指定要统计的文件的输入路径
8. 指定要输出的位置路径
9. 提交程序

1.6 Mapreduce的入门案例演示(重点,绘图)

1.6.1 案例简介:wordcount程序

wordcount:使用mr思想对多个文件,或者是多个块进行统计单词的出现频率

1.6.2 图解

 

 

1.6.4 wordcount案例代码步骤如下:

步骤1:创建项目,导入jar包依赖

<dependencies>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.7.6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-client -->
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>2.7.6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-hdfs -->
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>2.7.6</version>
    </dependency>
</dependencies>

步骤2:编写mapper类型

package com.qianfeng.mr.wordcount;
​
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;
​
/**
 * 1: 自定义类名,继承Mapper类型
 * 2: 定义K1,V1,K2,V2的泛型
 *       K1  行偏移量  整形   LongWritable     K1和V1是底层默认的类型,不能随便改
 *       V1  行内容    字符串  Text
 *       K2   单词     字符串  Text
 *       V2   数字1    整形   IntWritable
 * 3: 重写map方法
 */
public class MyMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
    Text k2 = new Text();
    IntWritable v2 = new IntWritable(1);
    /**
     *
     * @param key    就是k1    单词频率统计时,行偏移量没有任何用处,无需操心
     * @param value   就是v1   只需要对v1做截断处理即可
     * @param context    上下文,该对象提供了输出k2,v2的方法
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //1:将value转成java的String类型
        String lineRecord = value.toString();
        //2: 使用空格切分成数组
        String[] words = lineRecord.split(" ");
        //3:遍历数组,将元素作为K2,  数字1作为V2写出去
        for (String word : words) {
            //将word转成Text类型
            k2.set(word);
            //写出去
            context.write(k2,v2);
        }
    }
}
​

步骤3:编写reducer类型

package com.qianfeng.mr.wordcount;
​
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
​
import java.io.IOException;
​
/**
 * 1: 自定义类名,继承Reducer类型
 * 2: 规定k2,v2,k3,v3的泛型
 *       k2,v2和map阶段的输出数据的泛型一致
 *       k3:  单词    Text
 *       V3:  数字    IntWritable
 * 3: 重写reduce方法
 */
public class MyReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
    /**
     *      <hello,<1,1,1,1,1,1>>
     * @param key   就是k2
     * @param values   同一个k2的多个v2
     * @param context  上下文
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
         //1: 将values的多个1进行累加
        int sum = 0;
        for (IntWritable value : values) {
            //累加
            sum+=value.get();
        }
        //累加的结果sum就是v3, 转成IntWritable
        IntWritable v3 = new IntWritable(sum);
        //k2,就是k3,写出去
        context.write(key,v3);
    }
}
​

步骤4:编写driver类型

package com.qianfeng.mr.wordcount;
​
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 MyDriver {
    public static void main(String[] args) {
​
        try {
            Configuration configuration = new Configuration();
            Job job = Job.getInstance(configuration);
            //设置驱动类
            job.setJarByClass(MyDriver.class);
            //设置mapper和reducer类型
            job.setMapperClass(MyMapper.class);
            job.setReducerClass(MyReducer.class);
            //设置k2,v2,k3,v3的泛型
            job.setMapOutputKeyClass(Text.class);
            job.setMapOutputValueClass(IntWritable.class);
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(IntWritable.class);
            //设置分区数量,不设置默认为1
            job.setNumReduceTasks(1);
            //设置输入和输出路径
            Path inputPath = new Path("D:/test");
            FileInputFormat.addInputPath(job,inputPath);
            FileOutputFormat.setOutputPath(job,new Path("D:/output"));
​
            //提交等待完成
            boolean flag = job.waitForCompletion(true);
​
            System.exit(flag?0:1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
​

1.7 Partitioner组件的应用(重点)

1.7.1 reduceTask数量的研究

1. reduceTask的数量由分区数量决定
2. 不是每一个分区都有数据的
3. 如果自定义分区器了,那么reduceTask的数量不能小于分区器里的分区数量

1.7.2 分区器的简介与自定义

mapreduce的默认分区器是HashPartitioner。 逻辑如下:调用k2的hash值与int的最大值做位运算再与numReduceTasks做取模运算。
​
public class HashPartitioner<K, V> extends Partitioner<K, V> {
​
  /** Use {@link Object#hashCode()} to partition. */
  public int getPartition(K key, V value,
                          int numReduceTasks) {
    return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
  }
​
}
​
所以说,如果想要控制如何分区,可以自定义分区器,那么如何自定义分区器,可以参考HashPartitioner

案例演示

package com.qianfeng.mr.wordcount;
​
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
​
/**
 * 如何自定义分区器:
 * 1: 继承org.apache.hadoop.mapreduce.Partitioner
 * 2: 规定k2,v2的泛型
 * 3: 重写getPartition方法
 *     注意:该方法的返回值是一个int,表示分区号,是一个从0开始的自然数,多个分数号不能间断。必须是连续的
 *
 *
 *   需求: 将要统计的wordcount分为三个区:
 *         aA-nN为0分区的数据
 *         oO-zZ为1分区的数据
 *         其他为2分区的数据
 */
public class MyPartitioner extends Partitioner<Text, IntWritable> {
    @Override
    public int getPartition(Text text, IntWritable intWritable, int numPartitions) {
        //将k2变成字符串
        String k2 = text.toString();
        String first = k2.charAt(0)+"";
        if(first.matches("[A-Na-n]")){
            return 0;
        }else if(first.matches("[o-zO-Z]")) {
            return 1;
        }else{
            return 2;
        }
    }
}
​

1.8 IDE运行Mapreduce的几种模式(熟悉)

1.8.1 说明

集成开发工具(IDE)运行mapreduce的模式有三种,分别是
1. 在本地运行计算本地的文件,
2. 在本地运行计算HDFS上的文件,
3. 将计算程序远程提交到HDFS上计算HDFS上的文件。

1.8.2 本地运行计算本地的文件

本地指的是IDE所在的操作系统环境,计算的是本地文件系统中的文件。

原理如下:
- Configuration对象会读取四个默认的配置文件。
- fs.defaultFS的值为file:///,获取的是本地文件系统对象。
- mapreduce.framework.name的值为local

所以,在Driver中的输入路径和输出路径指定为本地的路径即可

1.8.3 本地运行计算HDFS上的文件

本地指的是IDE所在的操作系统环境,计算的是HDFS中的文件。

原理如下:
- 既然想要获取HDFS上的文件,那么fs.defaultFS的值为hdfs://qianfeng01:8020,也就是要获取分布式文件系统对象
- 是访问HDFS,将对应的文件读取到本地内存进行计算,计算结果存储到HDFS上(因为还是使用mapreudce.framework.name的默认值local).

1.8.4 本地远程提交计算程序到HDFS上,计算HDFS上的文件

原理:
- 移动计算而非移动数据
- 使用的是HDFS集群的设置,mapreduce.framework.name的值是yarn
- IDE需要有权限访问HDFS,IDE需要设置跨平台操作
   conf.set("mapreduce.app-submission.cross-platform", "true");
   System.setProperty("HADOOP_USER_NAME", "root");
   
步骤:
1. 将计算程序打包成jar文件,放入到classpath下: add as Library
2. 将集群的mapred-site.xml和yarn-site.xml文件导入到项目的resouces目录(注意重启的问题)
3. 如果报: 无任务控制....等字样,说明没有设置跨平台属性
4. 程序中的路径要使用hdfs上的路径。   

1.8.5 扩展:

将程序打成jar包,上传到hdfs上,使用hadoop jar指令来运行程序

注意:杀死yarn上的任务指令

]# yarn application -kill appId

appID就是网址8088上的ID

#

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值