MapReduce--分布式计算利器

来源:互联网 发布:c4d r18 mac 破解版 编辑:程序博客网 时间:2024/06/05 11:16

学习要点:

Ø MapReduce 原理★★★

Ø MapReduce 执行过程★★

Ø 数据类型与格式★★★

Ø Writable 接口与序列化机制★★★

 

1、MapReduce 是 Hadoop的核心组成,是专用于进行数据计算的。重点掌握实现 MapReduce 算法的步骤,掌握 map、reduce 函数的特点、如何写函数。

Python中的map和reduce  :map 翻译为“映射” ,reduce 翻译为“归约”,并非为hadoop专有。

 

2、Hadoop中的Map和Reduce

在 Hadoop 中 ,map 函 数 位 于 内 置 类org.apache.hadoop.mapreduce.Mapper<KEYIN,VALUEIN, KEYOUT, VALUEOUT>中,reduce函数位于内置类 org.apache.hadoop.mapreduce.

Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>中。 我们要做的就是覆盖 map 函数和reduce 函数。

 

对于 Hadoop 的 map 函数和 reduce 函数,处理的数据是键值对,也就是说 map 函数接收的数据是键值对,两个参数;输出的也是键值对,两个参数;reduce 函数接收的参数和输出的结果也是键值对。

 

Mapper 类,有四个泛型,分别是 KEYIN、VALUEIN、KEYOUT、VALUEOUT,前面两个 KEYIN、VALUEIN 指的是 map 函数输入的参数key、 value 的类型; 后面两个 KEYOUT、

VALUEOUT 指的是 map 函数输出的key、value 的类型。

Reducer 类,也有四个泛型,同理,分别指的是 reduce 函数输入的key、value类型,和输出的key、value 类型。

执行步骤:

 1. map任务处理

1.1 读取输入的HDFS文件内容,解析成key、value对。对输入文件的每一行,解析成key、value对。每一个键值对调用一次map函数。

1.2 覆盖map函数,实现自己的逻辑,对输入的key、value处理,转换成新的key、value输出。

1.3 对输出的key、value进行分区。

1.4 对不同分区的数据,按照key进行排序、分组。分组是指相同key的value放到一个集合中。

1.5 (可选)分组后的数据进行归约。

2.reduce任务处理

2.1 对多个map任务的输出,按照不同的分区,通过网络copy到不同的reduce节点,对reduce接收到的多个map任务的输出进行合并、排序。

2.2 覆盖reduce函数,实现自己的逻辑,对输入的key、values处理,转换成新的key、value输出。

2.3 把reduce的输出保存到HDFS文件中。

 

3、分析MapReduce执行过程

 

    MR过程各个角色的作用

    • Client:提交作业
      • 提交作业之前,需要对作业进行配置
        • 编写自己的MR程序
        • 配置作业,包括输入输出路径等等
      • 提交作业
        • 配置完成后,通过JobClient提交
      • 具体功能
        • 与JobTracker通信得到一个JobId
        • 输入输出路径检查
        • 将job拷贝到JobTracker的FS
        • 计算输入分片,将分片信息写入到job.split中
        • 写job.xml
        • 真正提交作业
    • JobTracker:初始化作业,分配作业,与TaskTracker通信,协调监控整个作业

      初始化作业:

      • 客户端提交作业后,JobTracker会将作业加入到队列,然后进行调度,默认是FIFO方式
      • 具体功能
        • 作业初始化主要是指JobInProgress中完成的
        • 读取分片信息
        • 创建task包括Map和Reduce任创建task包括Map和Reduce任务
        • 创建TaskInProgress执行task,包括map任务和reduce任务

      任务分配:

      • TaskTracker与JobTracker之间的通信和任务分配是通过心跳机制实现的
      • TaskTracker会主动定期向JobTracker发送心态信息,询问是否有任务要做,如果有,就会申请到任务。

       

    • TaskTracker:定期与JobTracker通信,执行Map和Reduce任务

    任务执行:

      • 如果TaskTracker拿到任务,会将所有的信息拷贝到本地,包括代码、配置、分片信息等
      • TaskTracker中的localizeJob()方法会被调用进行本地化,拷贝job.jar,jobconf,job.xml到本地
      • TaskTracker调用launchTaskForJob()方法加载启动任务
      • MapTaskRunner和ReduceTaskRunner分别启动java child进程来执行相应的任务

       

      状态更新:

      • Task会定期向TaskTraker汇报执行情况
      • TaskTracker会定期收集所在集群上的所有Task的信息,并向JobTracker汇报
      • JobTracker会根据所有TaskTracker汇报上来的信息进行汇总

       

    • HDFS:保存作业的数据、配置、结果

      作业完成;

      • JobTracker是在接收到最后一个任务完成后,才将任务标记为成功
      • 将数结果据写入到HDFS中

MapReduce 运行的时候,会通过 Mapper 运行的任务读取 HDFS 中的数据文件,然后调用自己的方法,处理数据,最后输出。Reducer 任务会接收 Mapper 任务输出的数据,作为自己的输入数据,调用自己的方法,最后输出到 HDFS 的文件中。

整个运行流程:

 

3.1、Mapper任务的执行过程

每个 Mapper 任务是一个 java 进程,它会读取HDFS 中的文件,解析成很多的键值对,经过我们覆盖的 map 方法处理后, 转换为很多的键值对再输出。 整个 Mapper 任务的处理过程又可以分为以下几个阶段,如图 :

第一阶段是把输入文件按照一定的标准分片(InputSplit),每个输入片的大小是固定的。默认情况下,输入片(InputSplit)的大小与数据块(Block)的大小是相同的。如果数据块(Block)的大小是默认值64MB,输入文件有两个,一个是 32MB,一个是72MB。那么小的文件是一个输入片,大文件会分为两个数据块,那么是两个输入片。一共产生三个输入片。每一个输入片由一个 Mapper 进程处理。这里的三个输入片,会有三个 Mapper 进程处理。

第二阶段是对输入片中的记录按照一定的规则解析成键值对。 有个默认规则是把每一行文本内容解析成键值对。 “键”是每一行的起始位置(单位是字节), “值”是本行的文本内容。

第三阶段是调用 Mapper 类中的 map 方法。 第二阶段中解析出来的每一个键值对, 调用一次 map 方法。如果有1000 个键值对,就会调用 1000 次 map 方法。每一次调用 map 方法会输出零个或者多个键值对。

第四阶段是按照一定的规则对第三阶段输出的键值对进行分区。比较是基于键进行的。比如我们的键表示省份(如北京、上海、山东等),那么就可以按照不同省份进行分区,同一个省份的键值对划分到一个区中。默认是只有一个区。分区的数量就是 Reducer 任务运行的数量。默认只有一个 Reducer 任务。

第五阶段是对每个分区中的键值对进行排序。首先,按照键进行排序,对于键相同的键值对,按照值进行排序。比如三个键值对<2,2>、<1,3>、<2,1>,键和值分别是整数。那么排序后的结果是<1,3>、<2,1>、<2,2>。如果有第六阶段,那么进入第六阶段;如果没有,直接输出到本地的 linux 文件中。

第六阶段是对数据进行归约处理,也就是 reduce 处理。键相等的键值对会调用一次reduce 方法。经过这一阶段,数据量会减少。归约后的数据输出到本地的 linxu 文件中。本阶段默认是没有的,需要用户自己增加这一阶段的代码。

 

3.2、Reducer任务的执行过程

每个 Reducer 任务是一个 java 进程。Reducer任务接收 Mapper 任务的输出,归约处理后写入到 HDFS 中,可以分为如图

第一阶段是 Reducer 任务会主动从 Mapper 任务复制其输出的键值对。 Mapper 任务可能会有很多,因此 Reducer 会复制多个Mapper 的输出。

第二阶段是把复制到 Reducer 本地数据,全部进行合并,即把分散的数据合并成一个大的数据。再对合并后的数据排序。

第三阶段是对排序后的键值对调用 reduce 方法。 键相等的键值对调用一次 reduce 方法,每次调用会产生零个或者多个键值对。最后把这些输出的键值对写入到 HDFS 文件中。在整个 MapReduce 程序的开发过程中,我们最大的工作量是覆盖 map 函数和覆盖reduce函数

4、键值对的编号

对于 Mapper 任务输入的键值对,定义为 key1 和 value1。在 map 方法中处理后,输出的键值对,定义为 key2 和 value2。reduce 方法接收 key2 和 value2,处理后,

输出 key3 和 value3。在下文讨论键值对时,可能把 key1 和 value1 简写为<k1,v1>,key2 和value2 简写为<k2,v2>,key3 和 value3 简写为<k3,v3>。

 

5、MapReduce的小型案例:单词计数:

分析思路:最直观的想法是使用数据结构 Map。解析文件中出现的每个单词,用单词作为 key,出现次数作为 value。 这个思路没有问题,但是在大数据环境下就不行了。我们需要使用MapReduce来做。 根据Mapper任务和Reducer任务的运行阶段, 我们知道在Mapper任务的第二阶段是把文件的每一行转化成键值对,那么第三阶段的 map 方法就能取得每一行文本内容,我们可以在 map 方法统计本行文本中单词出现的次数,把每个单词的出现次数作为新的键值对输出。在 Reducer 任务的第二阶段会对 Mapper 任务输出的键值对按照键进行排序,键相等的键值对会调用一次 reduce 方法。在这里,“键”就是单词, “值”就是出现次数。因此可以在 reduce 方法中对单词的不同行中的所有出现次数相加,结果就是该单词的总的出现次数。

 

覆盖map:

static class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable>{

//key2 表示该行中的单词

final Text key2 = new Text();

//value2 表示单词在该行中的出现次数

final IntWritable value2 = new IntWritable(1);

//key 表示文本行的起始位置

//value 表示文本行

protected void map(LongWritable key, Text value, Context context) throws java.io.IOException ,InterruptedException {

final String[] splited = value.toString().split(" ");

for (String word : splited) {

key2.set(word);

//把key2、value2写入到context中

context.write(key2, value2);

}

};

}

 

覆盖reduce

static class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable>{

//value3表示单词出现的总次数

final IntWritable value3 = new IntWritable(0);

/**

* key 表示单词

* values 表示map方法输出的1的集合

* context 上下文对象

*/

protected void reduce(Text key, java.lang.Iterable<IntWritable> values, Context context) throws java.io.IOException ,InterruptedException {

int sum = 0;

for (IntWritable count : values) {

sum += count.get();

}

//执行到这里,sum表示该单词出现的总次数

//key3表示单词,是最后输出的key

final Text key3 = key;

//value3表示单词出现的总次数,是最后输出的value

value3.set(sum);

context.write(key3, value3);

};

}

 

驱动代码:

/**

* 驱动代码

*/

public static void main(String[] args) throws IOException,InterruptedException, ClassNotFoundException {

//输入路径

final String INPUT_PATH = "hdfs://hadoop0:9000/input";

//输出路径,必须是不存在的

final String OUTPUT_PATH = "hdfs://hadoop0:9000/output";

//创建一个job对象,封装运行时需要的所有信息

final Job job = new Job(new Configuration(),"WordCountApp");

//如果需要打成jar运行,需要下面这句

job.setJarByClass(WordCountApp.class);

//告诉job执行作业时输入文件的路径

FileInputFormat.setInputPaths(job, INPUT_PATH);

//设置把输入文件处理成键值对的类

job.setInputFormatClass(TextInputFormat.class);

//设置自定义的Mapper类

job.setMapperClass(MyMapper.class);

//设置map方法输出的k2、v2的类型

job.setMapOutputKeyClass(Text.class);

job.setMapOutputValueClass(IntWritable.class);

//设置对k2分区的类

job.setPartitionerClass(HashPartitioner.class);

//设置运行的Reducer任务的数量

job.setNumReduceTasks(1);

//设置自定义的Reducer类

job.setReducerClass(MyReducer.class);

//设置reduce方法输出的k3、v3的类型

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(IntWritable.class);

//告诉job执行作业时的输出路径

FileOutputFormat.setOutputPath(job, new Path(OUTPUT_PATH));

//指明输出的k3类型

job.setOutputKeyClass(Text.class);

//指明输出的v3类型

job.setOutputValueClass(IntWritable.class);

//让作业运行,直到运行结束,程序退出

job.waitForCompletion(true);

}

 

 

以上代码的运行方式有两种,一种是在宿主机的 eclipse 环境中运行,一种是打成 jar包在 linux 中运行。

第一种运行方式要求宿主机能够访问 linux,并且对于输入路径和输出路径中的主机名hadoop0 , 要 在 宿 主 机 的 hosts 文 件 中 有 绑 定 , 笔 者 的 hosts 文 件 位 于

C:\WINDOWS\system32\drivers\etc 文件夹。

第二种运行方式,需要把代码打成 jar 包,在linux 下执行命令 hadoop jar xxx.jar 运行,如下图

 

6、hadoop的数据类型

基本数据类型:

java 中的基本类型有char、byte、boolean、short、int、float、double 共 7 中基本类型,除了 char,都有对应的Writable 类型。对于 int 和 long 除了 IntWritable、LongWritable 外,还有对应的VintWritable、VlongWritable。

除此类型之外, 还有字符串类型 Text、 字节数组类型 BytesWritable、 空类型 NullWritable、对象类型 ObjectWritable。以上这些类型构成了 mapreduce 运算的基本类型。

这些数据类型都有一个共同的特点,就是实现了org.apache.hadoop.io.Writable 接口。

 

集合数据类型:SequenceFile可以把大量小文件一起放到一个 block 中。在存储相同数量的文件时,可以明显减少 block 的数量。

 

7、hadoop jar XX.jar 源路径 目标路径(指定main方法)

hadoop jar xx.jar 主函数 源路径 目标路径(未指定main方法)

 

8、序列化

  • 序列化(Serialization)是指把结构化对象转化为字节流。
  • 反序列化(Deserialization)是序列化的逆过程。即把字节流转回结构化对象。
  • Java序列化(java.io.Serializable)

hadoop的序列化格式特点:

  • 紧凑:高效使用存储空间。
  • 快速:读写数据的额外开销小
  • 可扩展:可透明地读取老格式的数据
  • 互操作:支持多语言的交互

Hadoop的序列化格式:Writable

hadoop序列化的作用:

  • 序列化在分布式环境的两大作用:进程间通信,永久存储。
  • Hadoop节点间通信。


 

0 0
原创粉丝点击