Hadoop之MapReduce概述

MapReduce定义

MapReduce是一个分布式运算程序的编程框架是用户开发“基于Hadoop的数据分析应用”的核心框架。

MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序并发运行在一个Hadoop集群上。

MapReduce优缺点

优点
1MapReduce易于编程

它简单的实现一些接口就可以完成一个分布式程序这个分布式程序可以分布到大量廉价的PC机器上运行。也就是说你写一个分布式程序跟写一个简单的串行程序是一模一样的。就是因为这个特点使得MapReduce编程变得非常流行。

2良好的扩展性

当你的计算资源不能得到满足的时候你可以通过简单的增加机器来扩展它的计算能力。

3高容错性

MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上这就要求它具有很高的容错性。比如其中一台机器挂了它可以把上面的计算任务转移到另外一个节点上运行不至于这个任务运行失败而且这个过程不需要人工参与而完全是由Hadoop内部完成的。

4适合PB级以上海量数据的离线处理

可以实现上千台服务器集群并发工作提供数据处理能力。

缺点
1不擅长实时计算

MapReduce无法像MySQL一样在毫秒或者秒级内返回结果。

2不擅长流式计算

流式计算的输入数据是动态的而MapReduce的输入数据集是静态的不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。

3不擅长DAG有向无环图计算

多个应用程序存在依赖关系后一个应用程序的输入为前一个的输出。在这种情况下MapReduce并不是不能做而是使用后每个MapReduce作业的输出结果都会写入到磁盘会造成大量的磁盘IO导致性能非常的低下。

MapReduce核心思想

在这里插入图片描述
1分布式的运算程序往往需要分成至少2个阶段。

2第一个阶段的MapTask并发实例完全并行运行互不相干。

3第二个阶段的ReduceTask并发实例互不相干但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出。

4MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段如果用户的业务逻辑非常复杂那就只能多个MapReduce程序串行运行。

MapReduce进程

一个完整的MapReduce程序在分布式运行时有三类实例进程

1MrAppMaster负责整个程序的过程调度及状态协调。

2MapTask负责Map阶段的整个数据处理流程。

3ReduceTask负责Reduce阶段的整个数据处理流程。

MapReduce编程规范

用户编写的程序分成三个部分Mapper、Reducer和Driver。

1Mapper阶段

1用户自定义的Mapper要继承自己的父类

2Mapper的输入数据是KV对的形式KV的类型可自定义

3Mapper中的业务逻辑写在map()方法中

4Mapper的输出数据是KV对的形式KV的类型可自定义

5map()方法MapTask进程对每一个<K,V>调用一次

2Reducer阶段

1用户自定义的Reducer要继承自己的父类

2Reducer的输入数据类型对应Mapper的输出数据类型也是KV

3Reducer的业务逻辑写在reduce()方法中

4ReduceTask进程对每一组相同k的<k,v>组调用一次reduce()方法

3Driver阶段

相当于YARN集群的客户端用于提交我们整个程序到YARN集群提交的是封装了MapReduce程序相关运行参数的job对象

MapTask并行度决定机制

数据块Block是HDFS物理上把数据分成一块一块。数据块是HDFS存储数据单位。

数据切片数据切片只是在逻辑上对输入进行分片并不会在磁盘上将其切分成片进行存储。数据切片是MapReduce程序计算输入数据的单位一个切片会对应启动一个MapTask。
在这里插入图片描述

ReduceTask并行度决定机制

ReduceTask的并行度同样影响整个Job的执行并发度和执行效率但与MapTask的并发数由切片数决定不同ReduceTask数量的决定是可以直接手动设置

// 默认值是1手动设置为4
job.setNumReduceTasks(4);

1ReduceTask=0表示没有Reduce阶段输出文件个数和Map个数一致。

2ReduceTask默认值就是1所以输出文件个数为一个。

3如果数据分布不均匀就有可能在Reduce阶段产生数据倾斜

4ReduceTask数量并不是任意设置还要考虑业务逻辑需求有些情况下需要计算全局汇总结果就只能有1个ReduceTask。

5具体多少个ReduceTask需要根据集群性能而定。

mapreduce中job的提交流程

mapreduce中job提交流程

其中切片的过程是调用input.getSplits(job)这里的input是FileInputFormat对象实现流程如下
1程序先找到你数据存储的目录。

2开始遍历处理规划切片目录下的每一个文件

3遍历第一个文件ss.txt

a获取文件大小fs.sizeOf(ss.txt)
b计算切片大小
computeSplitSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M
c默认情况下切片大小=blocksize
d开始切形成第1个切片ss.txt—0:128M 第2个切片ss.txt—128:256M 第3个切片ss.txt—256M:300M每次切片时都要判断切完剩下的部分是否大于块的1.1倍不大于1.1倍就划分一块切片
e将切片信息写到一个切片规划文件中
f整个切片的核心过程在getSplit()方法中完成
gInputSplit只记录了切片的元数据信息比如起始位置、长度以及所在的节点列表等。
4提交切片规划文件到YARN上YARN上的MrAppMaster就可以根据切片规划文件计算开启MapTask个数。

切片机制

1简单地按照文件的内容长度进行切片

2切片大小默认等于Block大小

3切片时不考虑数据集整体而是逐个针对每一个文件单独切片

MapReduce工作流程

在这里插入图片描述
在这里插入图片描述
上面的流程是整个MapReduce最全工作流程但是Shuffle过程只是从第7步开始到第16步结束具体Shuffle过程详解如下

1MapTask收集我们的map()方法输出的kv对放到内存缓冲区中

2从内存缓冲区不断溢出本地磁盘文件可能会溢出多个文件

3多个溢出文件会被合并成大的溢出文件

4在溢出过程及合并的过程中都要调用Partitioner进行分区和针对key进行排序

5ReduceTask根据自己的分区号去各个MapTask机器上取相应的结果分区数据

6ReduceTask会抓取到同一个分区的来自不同MapTask的结果文件ReduceTask会将这些文件再进行合并归并排序

7合并成大文件后Shuffle的过程也就结束了后面进入ReduceTask的逻辑运算过程从文件中取出一个一个的键值对Group调用用户自定义的reduce()方法

注意
1Shuffle中的缓冲区大小会影响到MapReduce程序的执行效率原则上说缓冲区越大磁盘io的次数越少执行速度就越快。
2缓冲区的大小可以通过参数调整参数mapreduce.task.io.sort.mb默认100M。

shuffle机制

在这里插入图片描述
mapreduce shuffle机制原理详解

分区partition

要求将统计结果按照条件输出到不同文件中分区。比如将统计结果按照手机归属地不同省份输出到不同文件中分区

默认分区方式是根据key的hashCode对ReduceTasks个数取模得到的。用户没法控制哪个key存储到哪个分区。

即默认分区数量等于reducetask数量。
如果要自定义分区需要继承 Partitioner<Text, FlowBean> 然后重写int getPartition(Text key, FlowBean value, int numPartitions)方法在该方法内可以决定怎么分区分区数量等。

分区数和ReduceTask的关系
1如果ReduceTask的数量 > getPartition的结果数则会多产生几个空的输出文件part-r-000xx

2如果1 < ReduceTask的数量 < getPartition的结果数则有一部分分区数据无处安放会Exception

3如果ReduceTask的数量 = 1则不管MapTask端输出多少个分区文件最终结果都交给这一个ReduceTask最终也就只会产生一个结果文件 part-r-00000

4分区号必须从零开始逐一累加。

如果分区数不是1但是ReduceTask为1不执行分区过程。因为在MapTask的源码中执行分区的前提是先判断ReduceNum个数是否大于1。不大于1肯定不执行。

数据清洗ETL

“ETL是英文Extract-Transform-Load的缩写用来描述将数据从来源端经过抽取Extract、转换Transform、加载Load至目的端的过程。ETL一词较常用在数据仓库但其对象并不限于数据仓库

在运行核心业务MapReduce程序之前往往要先对数据进行清洗清理掉不符合用户要求的数据。清理的过程往往只需要运行Mapper程序不需要运行Reduce程序。

进一步分析MapTask和ReduceTask工作机制

MapTask工作机制

在这里插入图片描述
1Read阶段MapTask通过InputFormat获得的RecordReader从输入InputSplit中解析出一个个key/value。

2Map阶段该节点主要是将解析出的key/value交给用户编写map()函数处理并产生一系列新的key/value。

3Collect收集阶段在用户编写map()函数中当数据处理完成后一般会调用OutputCollector.collect()输出结果。在该函数内部它会将生成的key/value分区调用Partitioner并写入一个环形内存缓冲区中。

4Spill阶段即“溢写”当环形缓冲区满后MapReduce会将数据写到本地磁盘上生成一个临时文件。需要注意的是将数据写入本地磁盘之前先要对数据进行一次本地排序并在必要时对数据进行合并、压缩等操作。

溢写阶段详情

步骤1利用快速排序算法对缓存区内的数据进行排序排序方式是先按照分区编号Partition进行排序然后按照key进行排序。这样经过排序后数据以分区为单位聚集在一起且同一分区内所有数据按照key有序。

步骤2按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文件output/spillN.outN表示当前溢写次数中。如果用户设置了Combiner则写入文件之前对每个分区中的数据进行一次聚集操作。

步骤3将分区数据的元信息写到内存索引数据结构SpillRecord中其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大小超过1MB则将内存索引写到文件output/spillN.out.index中。

5Merge阶段当所有数据处理完成后MapTask对所有临时文件进行一次合并以确保最终只会生成一个数据文件。

当所有数据处理完后MapTask会将所有临时文件合并成一个大文件并保存到文件output/file.out中同时生成相应的索引文件output/file.out.index。

在进行文件合并过程中MapTask以分区为单位进行合并。对于某个分区它将采用多轮递归合并的方式。每轮合并mapreduce.task.io.sort.factor默认10个文件并将产生的文件重新加入待合并列表中对文件排序后重复以上过程直到最终得到一个大文件。

让每个MapTask最终只生成一个数据文件可避免同时打开大量文件和同时读取大量小文件产生的随机读取带来的开销。

ReduceTask工作机制

在这里插入图片描述
1Copy阶段ReduceTask从各个MapTask上远程拷贝一片数据并针对某一片数据如果其大小超过一定阈值则写到磁盘上否则直接放到内存中。

2Sort阶段在远程拷贝数据的同时ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并以防止内存使用过多或磁盘上文件过多。按照MapReduce语义用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序因此ReduceTask只需对所有数据进行一次归并排序即可。

3Reduce阶段reduce()函数将计算结果写到HDFS上。

数据压缩

mapreduce中涉及输入输出合理采用压缩能够提高io效率并且也能减少磁盘存储空间。
在mapreduce中采用压缩的环节及作用如下
在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Hadoop