MapReduce
简述mapreduce的整个流程
map端
MapTask 工作过程
- Read阶段
- Map阶段
- Collect阶段
- Spill阶段
- Combine阶段
- 由程序内的InputFormat来读取外部的数据,调用RecordReader的read方法来读取并,返回(k,v)键值对
- 读取的(k,v)键值对,传送给map()方法,作为其传入参数来执行用户自定义的map逻辑。
- context.write方法被调用时,outputcollector组件会将map()方法的输出结果写入到环形缓冲区
- 环形缓冲区就是一个环形数组,后端不断接收数据的同时,前端不断的溢出数据,长度用完后读取的新数据再从前端开始覆盖。这个缓冲区的默认size=100M,可以通过MR.SORT.MB配置
- spiller组件会从环形缓冲区溢出文件,这个过程会按照定义的partitioner分区(默认的是hashpartition),并且按照key.compareTo进行排序,如果有combiner也会执行combiner。spiller的不断工作,会溢出形成很多小文件(默认达到设置的80%时),会在本地创建溢出文件
- 小文件执行merge(合并),形成分区且区内排序
- Reduce会根据自己的分区,去所有map task中读取相对应的数据
reduce端
Reduce Task工作过程
- Copy阶段
- Merge阶段
- Sort阶段
- Reduce阶段
- Write阶段
- Reduce会从个个MapTask上远程复制一片数据,并针对某一片数据,如果大小超过一定的阈值,则会写到磁盘上,否则直接放到内存中。
- 通过GroupingComparator()分辨同一组的数据,把他们发送给reduce(k, iterator)方法(多个数据合成一组时,只取其中第一个key)
- 调用context.write方法,会让OutputFormat调用RecordWriter的write()方法将处理结果写入到数据仓库,每一个maptask对应一个分区文件。
shuffle机制
MapReduce工作过程中,Map阶段处理的的数据如何传递给Reduce阶段,shuffle会将MapTask输出的处理结果数据分发给ReduceTask,并在分发的过程中,对数据按照key进行分区和排序。
小文件问题
- 小文件处理的弊端:
- 增加map的开销,每一个分片都要执行一次map任务
- 增加作业的寻址速度
- 需要记录小文件的元数据,造成namenode内存浪费
- 解决方法
- 尽量避免小文件,将多个小文件事先合成一个顺序文件:将文件名作为key,将文件内容作为value
- 如果HDFS中有大批量的小文件,CombineFileInputFormat将多个小文件打包到一个分片中。
- 使用Hadoop自带的archive工具,主要减少namenode的负载
shuffle机制的缺陷
数据传输的IO问题:
- 每一个map可能会有多个spill文件需要写入磁盘,产生较多的磁盘IO
- 数据量很小,但是map和reduce任务很多时,产生较多得到网络IO
Combiner函数何时被使用
- Map端溢出spill文件后,进行分区排序后,会执行combiner函数。
- 多个spill文件合并成一个大文件时,会使用
- 在Reduce任务端,复制map输出,在合并后溢出到磁盘时,也会执行combiner函数
MapReduce 执行速度太慢,怎么办?
- 自定义分区函数,让key值较为均匀的分布在Reduce上
- 对map端进行压缩处理
- 使用combiner函数
reduce如何知道从那台机子上获取map的输出?
(MRAppMaster:任务调度和监控)
在Map和Reduce中间有一个MRAppMaster负责协调。MRAppMaster负责任务调度和监控,map结束会通知MRAppMaster,Reduce也会定期去询问MRAppMaster以便获取map输出的位置。
MapReduce如何性能调优?
有四个阶段可以进行性能调优:
- 数据输入阶段
- 小文件问题,产生很多map任务,任务的装载比较耗时,从而导致MapReduce运行速度较慢,可以采用CombineTextInputFormat来作为输入,来解决输入端大量的小文件场景
- Map阶段
- 减少溢写(spill)次数:通过调整io.sort.mb及sort.spiil.percent参数值,增大触发spiil内存上限,减少spill次数,从而缩短磁盘的I/O
- 减少合并(merge)次数:通过调整io.sort.factor参数,增大merge的文件数目,减少merge的次数,从而缩短mr处理时间
- 在map之后,不影响业务逻辑前提下,先进行combine处理,减少I/O
- Reduce阶段
- 合理设置map和reduce数:不能太少,也不能太多,太少导致task等待,延长处理时间;太多,导致map和reduce任务间竞争资源,造成处理超时等错误
- 设置map和reduce共存:调整slowstart.completedmaps参数,使map运行到一定程度后,reduce也开始运行,减少reduce的等待时间。
- 规避使用reduce:因为reduce在用于连接数据集的时候会产生大量的网络消耗。通过将MapReduce参数setNumReduceTasks设置为0来创建一个只有map的作业。
- 合理设置reduce端的buffer:默认情况下,当数据达到一定的阈值之后,buffer中的数据就会写入到磁盘,然后reduce从磁盘中进行读取所有数据。buffer和reduce是没有直接关联的,中间多一个写磁盘———>都磁盘的过程,既然有这个弊端,那么可以通过设置参数使buffer中的一部分数据直接输送到reduce,从而减少I/O开销。
- Shuffle阶段
- Shuffle阶段主要就是shuffle过程中尽量多的提供内存空间,防止内存溢出 参数mapred.child.java.opts来设置。