大数据系列(四)之 MapReduce过程及shuffle详解-博客
Hadoop学习之路(十三)MapReduce的初识 - 扎心了,老铁 - 博客园
MapReduce 架构:
在MapReduce中,用于执行MapReduce任务的机器角色有两个:JobTracker和TaskTracker。其中JobTracker是用于调度工作的,TaskTracker是用于执行工作的。一个Hadoop集群中只有一台JobTracker。
客户端向JobTracker提交一个作业,JobTracker把这个作业拆分成很多份,然后分配给TaskTracker去执行,TaskTracker会隔一段时间向JobTracker发送(Heartbeat)心跳信息,如果JobTracker在一段时间内没有收到TaskTracker的心跳信息,JobTracker会认为TaskTracker挂掉了,会把TaskTracker的作业任务分配给其他TaskTracker。
MapReduce执行过程:
1、客户端启动一个job
2、向JobTracker请求一个JobID
3、将运行作业所需要的资源文件复制到HDFS上,包括MapReduce程序打包的JAR文件、配置文件和客户端计算所得的输入划分信息。这些文件都存放在JobTracker专门为该作业创建的文件夹中,文件夹名为该作业JobID。JAR文件默认会有10个副本,输入划分信息告诉JobTracker应该为这个作业启动多少个map任务等信息。
4、JobTracker接收到作业后将其放在作业队列中,等待JobTracker对其进行调度。当JobTracker根据自己的调度算法调度该作业时,会根据输入划分信息为每个划分创建一个map任务,并将map任务分配给TaskTracker执行。这里需要注意的是,map任务不是随便分配给某个TaskTracker的,Data-Local(数据本地化)将map任务分配给含有该map处理的数据库的TaskTracker上,同时将程序JAR包复制到该TaskTracker上运行,但是分配reducer任务时不考虑数据本地化。
5、TaskTracker每隔一段时间给JobTracker发送一个Heartbeat告诉JobTracker它仍然在运行,同时心跳还携带很多比如map任务完成的进度等信息。当JobTracker收到作业的最后一个任务完成信息时,便把作业设置成“成功”,JobClient再传达信息给用户。
将 MapReduce
分为5个步骤的理解是:
- Prepare the Map() input:
MapReduce
框架先指定Map
处理器,然后给其分配将要处理的输入数据 -- 键值对K1
,并为该处理器提供与该键值相关的所有输入数据; - Run the user-provided Map() code:
Map()
在K1
键值对上运行一次,生成由K2
指定的键值对的输出; - Shuffle the Map output to the Reduce processors:将先前生成的
K2
键值对,根据『键』是否相同移至相同的工作节点; - Run the user-provided Reduce() code:对于每个工作节点上的
K2
键值对进行Reduce()
操作; - Produce the final output:
MapReduce
框架收集所有Reduce
输出,并按K2
对其进行排序以产生最终结果进行输出。
MapReduce中的Map和Reduce
MapReduce 框架只操作键值对,MapReduce 将job的不同类型输入当做键值对来处理并且生成一组键值对作为输出。
简单来说,Map就是“分”而Reduce就是“合” 。
(input) ->map-> ->combine-> ->reduce-> (output)
MapReduce的执行流程概括的来说是先将输入数据根据一定规则进行拆分(map阶段),再合并计算(reduce阶段)然后输出结果。
MapReduce程序处理的是HDFS上的数据,数据在HDFS上存储都是以Block块的形式存储的。
MapReduce程序通过InputFormat接口读取Block块上的数据,然后进行逻辑分片(Split )生成<key,value>键值对交给Map阶段处理,(一般情况下,一个Block块对应一个Split。)然后MapTask会处理逻辑分片Split上的数据。
不论是在map阶段还是在reduce阶段,数据的输入输出都是以<key,value>键值对的形式进行输入和输出的。
map: MapReduce中的每个map任务可以细分4个阶段:record reader、mapper、combiner和partitioner。map任务的输出被称为中间键和中间值,会被发送到reducer做后续处理。
(1)读取HDFS中的文件。每一行解析成一个<k,v>。每一个键值对调用一次map函数。<0,helloyou> <10,hello me>
(2)覆盖map(),接收(1)中产生的<k,v>,进行处理,转换为新的<k,v>输出。<hello,1><you,1><hello,1><me,1>
(3)对(2)输出的<k,v>进行分区,默认分为一个区。
(4)对不同分区中的数据进行按照Key排序、分组。分组指的是相同key的value放到一个集合中。排序后:<hello,1><hello,1><me,1> <you,1>,分组后:<hello,{1,1}><me,{1}><you,{1}>
(5)对分组后的数据进行合并归约。
reduce
reduce任务可以分为4个阶段:混排(shuffle)、排序(sort)、reducer和输出格式(output format)
(1)多个map任务的输出,按照不同的分区,通过网络copy到不同的reduce节点上.(shuffle)
(2)对多个map的输出进行合并、排序。覆盖reduce函数,接收的是分组后的数据,实现自己的业务逻辑,<hello,2><me,1><you,1>处理后,产生新的<k,v>输出。
(3)对reduce输出的<k,v>写到HDFS中。
map阶段的shuffle可以划分为以下四步:
①分区partition
②写入环形内存缓冲区
③执行溢出写
排序sort--->合并combiner--->生成溢出写文件
④归并merge
reduce阶段的shuffle可以划分为以下三步:
①复制copy
②归并merge
③reduce
几个概念:
- 环形缓冲区:在map任务处理完之后,首先会将这个结果写入到内存缓冲区当中,也就是在内存中会划分出一片区域来存储map任务处理完的数据结果,我们将这个缓冲区称为环形缓冲区。内存缓冲区的大小是有限的,默认是100MB,我们可以通过配置文件来设置其大小。缓冲区的作用就是批量收集Map结果,减少磁盘I/O影响。
- 环形缓冲区的默认大小为100M,缓冲区会设置一个写入的阈值(80%),达到或超过阈值时会触发磁盘写入的操作,写入磁盘后环形缓冲区会进行重新清空,以便有足够的空间接收maptask 新的输出结果。 设置阈值的作用是在达到阈值后,数据写出到磁盘的同时,maptask 还可以继续向环形缓冲区写入数据。
- 溢写:当MapTask输出结果有很多时,内存可能会不足,所以需要在一定条件下将缓冲区中的数据临时写入磁盘,然后重新利用这个缓冲区。这个从内存往磁盘写数据的过程被称为Spill,中文译为溢写。每一次溢写都会在磁盘上生成一个溢写文件,如果Map输入结果很大,就会有多次这样的溢写发生,磁盘上就会有多个溢写文件存在。当MapTask真正完成时,内存缓冲区中的数据将全部溢写到磁盘中形成一个溢写文件。因为最终的文件只有一个,所以需要将这些溢写文件归并(使用归并算法)到一起,这个过程就是Merge。至此,Map端所有工作都已结束。
- MapReduce提供了Partitioner接口,默认是HashPartitioner。(其作用是根据Key或Value及Reduce的数量来决定当前这对输出数据最终应该交由哪个ReduceTask处理。)
- 分区内排序:从缓冲区写到磁盘的时候,会进行分区并排序,分区指的是某个key应该进入到哪个分区,同一分区中的key会进行排序,如果定义了Combiner的话,也会进行combine操作。
- 每个ReduceTask不断地通过RPC(HTTP协议)从JobTracker(Hadoop2.0有了Yarn之后,是ResourceManager)那里获取MapTask是否完成信息。如果ReduceTask获知某台TaskTraker(Hadoop2.x中为AppMaster)上的MapTask执行完成,那么Shuffle的后半段过程开始启动。
- Reduce 复制copy:每个reducer对应一个ReduceTask,在真正开始reduce之前,先要从mapTask的数据文件中分区中拷贝属于自己的分区数据,在拷贝之后还会伴随着排序(sort)、合并(Merge)。
- Merge 阶段:同Map端的Merge动作,只是数组中存放的是不同Map端复制过来的数据。复制过来 数据会先放到内存缓冲区中,当内存中的数据达到一定阈值时,就会启动内存到磁盘的Merge。与Map端类似,这也是溢写过程,会在磁盘中形成众多的溢写文件,然后将这些溢写文件进行归并。
- Reducer 的输出文件:不断地进行Merge后,最后会生成一个“最终文件”。这个文件可能存放在磁盘,也可能存放在内存中,默认存放在磁盘上。当Reducer的输入文件已定时,整个Shuffle过程才最终结束。
************************************************************************************
Partition 分区:默认分区是根据key的hashCode对ReduceTasks个数取模得到的。
(key的hashCode进行%reducTask运算)