MapReduce是Hadoop核心编程模型,在Hadoop中,数据处理核心就是MapReduce程序设计模型。
MapReduce编程模型
MapReduce计算过程分为Map阶段和Reduce阶段。
Map:进行数据的读取和预处理
Reduce:合并
MapReduce执行流程
1.输入和拆分
(该部分其实不属于map和reduce的主要过程,但是也属于整个计算框架消耗时间的一部分,并且这一部分给map准备合适的数据,因此非常重要)
split操作
split只是将源文件的内容分片形成一系列的InputSplit,每个 InputSpilt 中存储着对应分片的数据信息(例如,文件块信息、起始位置、数据长度、所在节点列表…),每个InputSplit 都由一个mapper 进行后续处理。
分片大小的参数
参数 | 解释 | 备注 |
---|---|---|
minSize | splitSize的最小值 | 由mapred-site.xml配置文件中的mapred.min.split.size参数确定 |
maxSize | splitSize的最大值 | 由 mapred-site.xml 配置文件中mapreduce.jobtracker.split.metainfo.maxsize 参数确定 |
blockSize | HDFS中文件储存的块大小 | 由hdfs-site.xml 配置文件中dfs.block.size 参数确定 |
s p l i t S i z e = m a x { m i n S i z e , m i n { m a x S i z e , b l o c k S i z e } } splitSize=max\{minSize,min\{maxSize,blockSize\}\} splitSize=max{minSize,min{maxSize,blockSize}}
数据格式化(format)操作
将划分好的InputSplit 格式化成键值对形式的数据。
其中 key为偏移量,value是每一行的内容。
注意Map和数据格式化操作并不存在前后时间差,而是同时进行的。
2. Map映射
这一过程中充分利用了Hadoop并行的特性,根据用户指定的Map过程,Mapreduce尝试在数据在HDFS中,文件数据是被复制多份的,所以计算将会选择拥有此数据的最空闲的节点。
这一步骤用户可具体自定义map的任务。
3. Shuffle派发
Shuffle 过程是指 Mapper 产生的直接输出结果,经过一系列的处理,成为最终的Reducer直接输入数据为止的整个过程。这是mapreduce的核心过程。
Mapper端的Shuffle: Mapper产生的结果会先储存在内存中,当内存中的数据量达到设定的阈值时会一次性写入本地磁盘。并同时运行:
- sort (排序):将Mapper产生的结果按照key值进行排序
- combine(合并):把key值相同的记录进行合并
- partition(分片):把数据均衡的分配给Reducer
Reduce端的Shuffle: 由于Mapper 和Reducer 往往不在同一个节点上运行,所以Reducer 需要从多个节点上下载 Mapper 的结果数据,并对这些数据进行处理,然后才能被Reducer 处理。
4. Reduce缩减
Reducer 接收形式的数据流,形成形式的输出,具体的过程可以由用户自定义,最终结果直接写入 hdfs。每个 reduce 进程会对应一个输出文件,名称以part-开头。
MapReduce数据本地化(Data-Local)
HDFS是海量数据存储的基础,而MapReduce则是在数据的上一层通过编写MapReduce程序对海量数据进行计算处理。
HDFS | 解释说明 | MapReduce | 解释说明 |
---|---|---|---|
NameNode | 文件系统的名字节点进程/负责存储任务调度 | JobTracker | 负责计算任务调度 |
DataNode | 文件系统的数据节点进程 /负责存储数据 | TaskTracker | 负责真正计算任务 |
考虑到“本地化原则”,一般地,将 NameNode 和 JobTracker部署到同一台机器上,各个 DataNode 和TaskTracker 也同样部署到同一台机器上。
这样做的目的是将 map 任务分配给含有该 map 处理的数据块的 TaskTracker 上,同时将程序JAR 包复制到该TaskTracker 上来运行,这叫运算移动,数据不移动 。而分配reduce任务时并不考虑数据本地化。
总体来讲,数据本地化顾名思义,尽量在最少的对数据进行操作的基础上,完成对数据的处理、挖掘和分析任务。
MapReduce工作原理
通过 Client、JobTracker 和 TaskTracker分析:
- Client启动一个Job并向JobTracker请求一个JobID;
- 将运行作业所需要的资源文件复制到HDFS上(包括MP程序打包的JAR文件、配置文件和客户端计算所得输入划分信息);
- JobTracker为这些文件创建Job ID文件夹, JAR文件默认会有10个副本(mapred.submit.replication 属性控制);
- 输入划分信息告诉了 JobTracker应该为这个作业启动多少个 map任务等信息;
- JobTracker将接受到的作业放置在一个作业队列里,等待作业调度器对其进行调度;
- 作业调度器根据自己的调度算法调度到该作业时,会根据输入划分信息为每个划分创建一个 map 任务,并将 map 任务分配给 TaskTracker 执行。
- 。对于 map 和 reduce 任务,TaskTracker根据主机核的数量和内存的大小有固定数量的map槽和 reduce 槽(map 任务不是随便地分配给某个TaskTracker 的,这里就涉及到上面提到的数据本地化);
- TaskTracker每隔一段时间会向JobTracker发送一个心跳,确认任务仍在进行并返回相应的信息(如map任务完成的进度);
- 。当JobTracker 收到作业的最后一个任务完成信息时,便把该作业设置成“成功” 。
通过Map和Reduce端分析:
Map端流程:
- 每个输入分片会让一个map任务来处理,处理结果暂时会放置在一个环形内存缓冲区中,当缓冲区快要溢出(80%)时会将缓冲区内的数据,写入在本地文件系统中创建的一个溢出文件中;
- 在写入磁盘之前,线程首先根据reduce任务的数目将数据划分为相同数目的分区,即一个reduce任务对应一个分区的数据。这是为了避免有些 reduce任务分配到大量数据,而有些reduce任务却分到很少数据,甚至没有。其实分区就是对数据进行 hash 的过程。然后对每个分区中的数据进行排序,若此时有Combiner,将排序后的结果进行Combine 操作,这样做的目的是让尽可能少的数据写入到磁盘。
- map任务输出最后一个记录时,可能会有很多的溢出文件,这时需要将这些文件合并。合并的过程中会不断地进行排序和Combine操作,目的是:尽量减少每次写入磁盘的数据量和下一复制阶段网络传输的数据量。 最后合并成了一个已分区且已排序的文件。(为了减少网络传输的数据量,这里可以将数据压缩,只要将mapred.compress.map.out 设置为true。)
- 将分区中的数据拷贝给相对应的 reduce 任务。分区对应的reduce向JobTracker获取对应的map输出位置。map则通过TaskTracker再通过心跳与JobTracker相连。。所以 JobTracker中保存了整个集群中的宏观信息。
Reduce端流程:
- Reduce接受到不同map传来的有序数据,若数据量相当小则直接存储在内存中,如果数据量超过了缓冲区一定的比例则对数据合并和溢写到磁盘中。
- 后台线程将溢写文件合并成为一个更大的有序文件。这样是为了给后面的合并节省时间。MP中会反复的执行排序,所以Hadoop中排序非常重要。
- 合并的过程中会产生许多的中间文件(写入磁盘了),但MapReduce 会让写入磁盘的数据尽可能地少,并且最后一次合并的结果并没有写入磁盘,而是直接输入到reduce函数。
在Map 处理数据后,到Reduce 得到数据之前,这个流程在 MapReduce中可以看做是一个Shuffle 的过程。
Mapper运行后产生的key/values键对应该立刻决定交由哪个reduce处理。
MapReduce 提供Partitioner 接口,它的作用就是根据 key 或 value 及 reduce 的数量来决定当前的这对输出数据最终应该交由哪个 reduce task 处理。默认对 key 做 hash 后再以 reduce task 数量取模。
Mapreduce 一个小的应用
传统的Hash应用:流量分发
在大数据时代,网站的流量访问数据如果仅在单独一台服务器处理的话,极有可能造成网站的崩溃,造成不必要的损失,因此通过将不同的访问流量均匀分布在多个服务器上进行处理的这种流量分发的方式成为了很多互联网公司的首选,从某种角度上讲这就是分布式的应用。
传统Hash方法即可以用在流量分发上,其原理非常简单。就是利用求余的方法。假设现在有 n n n 台机器,只需要将引入的流量的key(可以是cookie,也可以是均匀随机值等)求Hash后按对n求余数,得到的值即为流量分配的机器。
如:设总计8台机器,对应第289次访问的流量将被分配至:
289
%
8
=
1
289 \% 8 = 1
289%8=1
第一台机器。
一致性Hash:支持动态增长
一致性哈希算法设计目标是为了解决因特网中的热点问题。
参考文献
- 《Hadoop大数据实战手册》.林中天
- 《Hadoop技术内幕:深入解析Hadoop Common和HDFS架构设计与实现原理》.蔡斌、陆湘萍
- 《Hadoop权威指南(第2版)》