一、原理
设计MapReduce
的出发点就是为了解决如何把大问题分解成独立的小问题,再并行解决。
MapReduce的架构图:
- Client
Client
的含义是指用户使用MapReduce
程序通过Client
来提交任务到Job Tracker
上,同时用户也可以使用Client
来查看一些作业的运行状态。
- Job Tracker
这个负责的是资源监控和作业调度。JobTracker
会监控着TaskTracker
和作业的健康状况,会把失败的任务转移到其他节点上,同时也监控着任务的执行进度、资源使用量等情况,会把这些消息通知任务调度器,而调度器会在资源空闲的时候选择合适的任务来使用这些资源。
任务调度器是一个可插拔的模块,用户可以根据自己的需要来设计相对应的调度器。
- TaskTracker
TaskTracker
会周期性地通过Hearbeat
来向Job Tracker
汇报自己的资源使用情况和任务的运行进度。会接受来自于JobTaskcker
的指令来执行操作(例如启动新任务、杀死任务之类的)。
在TaskTracker
中通过的是slot
来进行等量划分一个节点上资源量,只用Task
获得slot
的时候才有机会去运行。调度器的作用就是进行将空闲的slot
分配给Task
使用,可以配置slot
的数量来进行限定Task上的并发度。
- Task
Task
分为Map Task
和Reduce Task
,在MapReduce
中的 split
就是一个 Map Task
,split
的大小可以设置的,由 mapred.max.spilt.size
参数来设置,默认是 Hadoop
中的block
的大小,在Hadoop 2.x
中默认是128M
,在Hadoop 1.x
中默认是64M
。
在Task
中的设置可以这么设置,一般来讲,会把一个文件设置为一个split
,如果是小文件,那么就会存在很多的Map Task
,这是特别浪费资源的,如果split
切割的数据块的量大,那么会导致跨节点去获取数据,这样也是消耗很多的系统资源的。
MapReduce的生命周期
一共分为5个步骤:
1. 作业的提交和初始化
由用户提交作业之前,需要先把文件上传到HDFS
上,JobClient
使用upload
来加载关于打包好的jar
包,JobClient
会RPC
创建一个JobInProcess
来进行管理任务,并且创建一个TaskProcess
来管理控制关于每一个Task
。
2. JobTracker调度任务
JobTracker
会调度和管理任务,一发现有空闲资源,会按照一个策略选择一个合适的任务来使用该资源。
任务调度器有两个点:一个是保证作业的顺利运行,如果有失败的任务时,会转移计算任务,另一个是如果某一个Task
的计算结果落后于同一个Task
的计算结果时,会启动另一个Task
来做计算,最后去计算结果最块的那个。
3. 任务运行环境
TaskTracker
会为每一个Task来准备一个独立的JVM
从而避免不同的Task
在运行过程中的一些影响,同时也使用了操作系统来实现资源隔离防止Task
滥用资源。
4. 执行任务
每个Task
的任务进度通过RPC
来汇报给TaskTracker
,再由TaskTracker
汇报给JobTracker
。
5. 任务结束,写入输出的文件到HDFS
中。
二、流程
分片、格式化数据源
输入 Map 阶段的数据源,必须经过分片和格式化操作。
分片操作:指的是将源文件划分为大小相等的小数据块( Hadoop 2.x 中默认 128MB ),也就是分片( split ),
Hadoop 会为每一个分片构建一个 Map 任务,并由该任务运行自定义的 map() 函数,从而处理分片里的每一条记录;
格式化操作:将划分好的分片( split )格式化为键值对<key,value>形式的数据,其中, key 代表偏移量, value 代表每一行内容。
执行 MapTask
每个 Map 任务都有一个内存缓冲区(缓冲区大小 100MB ),输入的分片( split )数据经过 Map 任务处理后的中间结果会写入内存缓冲区中。
如果写人的数据达到内存缓冲的阈值( 80MB ),会启动一个线程将内存中的溢出数据写入磁盘,同时不影响 Map 中间结果继续写入缓冲区。
在溢写过程中, MapReduce 框架会对 key 进行排序,如果中间结果比较大,会形成多个溢写文件,最后的缓冲区数据也会全部溢写入磁盘形成一个溢写文件,如果是多个溢写文件,则最后合并所有的溢写文件为一个文件。
执行 Shuffle 过程
MapReduce 工作过程中, Map 阶段处理的数据如何传递给 Reduce 阶段,这是 MapReduce 框架中关键的一个过程,这个过程叫作 Shuffle 。
Shuffle 会将 MapTask 输出的处理结果数据分发给 ReduceTask ,并在分发的过程中,对数据按 key 进行分区和排序。
执行 ReduceTask
输入 ReduceTask 的数据流是<key, {value list}>形式,用户可以自定义 reduce()方法进行逻辑处理,最终以<key, value>的形式输出。
写入文件
MapReduce 框架会自动把 ReduceTask 生成的<key, value>传入 OutputFormat 的 write 方法,实现文件的写入操作。
————————————————
MapTask
Read 阶段: MapTask 通过用户编写的 RecordReader ,从输入的 InputSplit 中解析出一个个 key / value 。
Map 阶段:将解析出的 key / value 交给用户编写的 Map ()函数处理,并产生一系列新的 key / value 。
Collect 阶段:在用户编写的 map() 函数中,数据处理完成后,一般会调用 outputCollector.collect() 输出结果,在该函数内部,它会将生成的 key / value 分片(通过调用 partitioner ),并写入一个环形内存缓冲区中(该缓冲区默认大小是 100MB )。
Spill 阶段:即“溢写”,当缓冲区快要溢出时(默认达到缓冲区大小的 80 %),会在本地文件系统创建一个溢出文件,将该缓冲区的数据写入这个文件。
Combine 阶段:当所有数据处理完成以后, MapTask 会对所有临时文件进行一次合并,以确保最终只会生成一个数据文件
ReduceTask
1、Copy 阶段: Reduce 会从各个 MapTask 上远程复制一片数据(每个 MapTask 传来的数据都是有序的),并针对某一片数据,如果其大小超过一定國值,则写到磁盘上,否则直接放到内存中
2、Merge 阶段:在远程复制数据的同时, ReduceTask 会启动两个后台线程,分别对内存和磁盘上的文件进行合并,以防止内存使用过多或者磁盘文件过多。
3、Sort 阶段:用户编写 reduce() 方法输入数据是按 key 进行聚集的一组数据。
4、Reduce 阶段:对排序后的键值对调用 reduce() 方法,键相等的键值对调用一次 reduce()方法,每次调用会产生零个或者多个键值对,最后把这些输出的键值对写入到 HDFS 中
5、Write 阶段: reduce() 函数将计算结果写到 HDFS 上。
版权声明:本文为CSDN博主「Shockang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Shockang/article/details/117970151
————————————————————————————————————————
一个完整的mapreduce程序在分布式运行时有三类实例进程:
1)MrAppMaster:负责整个程序的过程调度及状态协调
2)MapTask:负责map阶段的整个数据处理流程
3)ReduceTask:负责reduce阶段的整个数据处理流程
工作全流程详解:
上面图一和图二中的流程是整个MapReduce最全工作流程,主要包括MapTask阶段、Shuffle阶段和ReduceTask阶段,而Shuffle阶段和MapTask阶段、ReduceTask阶段都存在交集,具体流程如下:
- 准备好待处理的文本
- 客户端submit()前,获取待处理数据的信息,然后根据参数配置形成一个任务分配的规划
- 客户端向Yarn集群提出请求创建Mr appmaster并提交切片等相关信息:job.split、wc.jar(集群模式才需要)、job.xml
- Yarn调用ResourceManager来创建Mr appmaster,而Mr appmaster则会根据切片的个数来创建几个Map Task。于是,MapTask进程开始工作。
- MapTask们从文件中读取各自需要处理的数据,默认是TextInputFormat格式(可以自定义)。其实是里面的RecorderReader来读取,每读取一行之后返回给Mapper
- 在Mapper中调用map()方法来对每一行数据进行相关的业务上的逻辑运算处理
- 在用户编写map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。而在该函数内部,它会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中(环形缓冲区默认大小是100M,数据处理的特点:左侧写索引或者是元数据信息,右侧写数据,写满80%后溢写到磁盘,然后反向又开始写,如此反复)。自数据进入到环形缓冲区后,Shuffle过程正式开始。
- 进入到环形缓冲区之后在溢写之前会对数据进行一次排序。排序的方式是,先按照分区编号Partition进行排序,然后按照key进行排序。这样,经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序。
- 排完序之后就溢出到文件(分区且区内有序),整个过程会多次溢出到多个文件
- 在所有数据都溢出到文件之后,开始Merge归并排序(对同一个分区内溢出的多个有序的结果文件合并成一个大的溢出文件且完成归并排序)
- 之后的Combiner合并为可选流程:分区内合并和压缩。之后,写入到磁盘。至此MapTask的执行过程基本结束。
- 在所有Map Task任务都完成之后,根据分区的数量来启动相应数量的Reduce Task,并告知ReduceTask处理数据范围(数据分区)(有几个分区就启动几个Reduce Task,每个Reduce Task专门处理同一个分区的数据,比如处理MapTask1中partition0和MapTask2中partition0的数据)
- ReduceTask根据自己的分区号,去各个MapTask机器上拷贝相应分区内的数据到本地内存缓冲区,缓冲区不够的话就溢写到磁盘。待所有数据拷贝完毕之后,ReduceTask会将这些文件再进行归并排序
- 排好序之后按照相同的key分组。至此Shuffle的过程基本结束。
- 在分组之后一次读取一组数据到Reducer,调用reduce()方法进行聚合处理
- 之后通过context.write默认以TextOutputFormat格式经RecordWriter下入到文件。最后,ReduceTask过程结束。
注意:
Shuffle中的缓冲区大小会影响到MapReduce程序的执行效率,原则上说,缓冲区越大,磁盘io的次数越少,执行速度就越快。缓冲区的大小可以通过参数调整,参数:io.sort.mb默认100M。
————————————————
版权声明:本文为CSDN博主「深圳四月红」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43230682/article/details/105353157
参考:
MapReduce系列:初识MapReduce的应用场景(附JAVA和Python代码) - spacedong的文章 - 知乎 https://zhuanlan.zhihu.com/p/58297932