MapReduce面试题整理与收集
序言
本文内容是整理和收集MapReduce基本问题,材料源于各类平台文章中,本文编写仅作为学习参看使用。
其中有CSDN博主「Dota_Data」,原文链接:https://blog.csdn.net/dota_data/article/details/93342876
一、MapReduce基本常识
shuffle流程概括
- 其在MapReduce中所处的工作阶段是map输出后到reduce接收前,具体可以分为map端和reduce端前后两个部分。
- 因为频繁的磁盘I/O操作会严重的降低效率,因此“中间结果”不会立马写入磁盘,而是优先存储到map节点的“环形内存缓冲区”,在写入的过程中进行分区(partition),也就是对于每个键值对来说,都增加了一个partition属性值,然后连同键值对一起序列化成字节数组写入到缓冲区(缓冲区采用的就是字节数组,默认大小为100M)。
- 当写入的数据量达到预先设置的阙值后(mapreduce.map.io.sort.spill.percent,默认0.80,或者80%)便会启动溢写出线程将缓冲区中的那部分数据溢出写(spill)到磁盘的临时文件中,并在写入前根据key进行排序(sort)和合并(combine,可选操作)。
- 当整个map任务完成溢出写后,会对磁盘中这个map任务产生的所有临时文件(spill文件)进行归并(merge)操作生成最终的正式输出文件,此时的归并是将所有spill文件中的相同partition合并到一起,并对各个partition中的数据再进行一次排序(sort),生成key和对应的value-list
- 文件归并时,如果溢写文件数量超过参数min.num.spills.for.combine的值(默认为3)时,可以再次进行合并。
- 对于reduce端的shuffle过程来说,reduce task在执行之前的工作就是不断地拉取当前job里每个map task的最终结果,然后对从不同地方拉取过来的数据不断地做merge最后合并成一个分区相同的大文件,然后对这个文件中的键值对按照key进行sort排序,排好序之后紧接着进行分组,分组完成后才将整个文件交给reduce task处理。
二、MapReduce要点
1、combiner的组件需要注意什么?
因为combiner在MapReduce过程中可能调用也可能不调用,可能调用一次也可能调用多次,无法确定和控制。
所以,combiner的使用原则是:有或没有都不能影响业务逻辑,是不是用combiner都不能影响最终reducer的结果。而且,combiner的输出kv应该跟reducer的输入kv对应起来。因为有时使用combiner不当的话会对统计结果造成错误结局,还不如不用。比如对所有数求平均数:
Mapper端使用combiner
3 5 7 ->(3+5+7)/3=5
2 6 -> ( 2+6)/3=4
Reducer
(5+4)/2=9/2≈4.5 不等于 (3+%+7+2+6)/5=23/5≈4.6
2、对于MapReduce的各个阶段你觉得有什么优化空间?
-
数据输入:默认情况下TextInputFormat对任务的切片是按文件切,无论文件大小,都会给一个单独的切面,交给一个maptask,这时如果输入的是大量小文件,就会产生大量的maptask,处理效率极低。最好的解决方法就是在预处理阶段将小文件合并,再上传到HDFS处理分析。但如果已经上传到HDFS了,就可以用另一种切片方法来补救,CombineTextinputFormat,它的切片逻辑和TextInputFormat不同,可以将多个小文件从逻辑上规划到一个切片中,然后把这些小文件交给maptask。
-
运行时间:启动一个MapReduce任务,map阶段和reduce阶段都会有并行的task共同处理任务,这些task都需要开jvm,然后初始化,而这些jvm很花费空间的,如果运行一个20-30s的任务需要进行开启,初始化,停止jvm操作很是浪费,所以我们应该尽量吧数据量控制在能让每个task运行1分钟以上。
-
数据倾斜:可以通过对原始数据进行抽样得到结果集来预设分区。
3、MapReduce怎么实现TopN?
可以自定义GroupingComparator,对结果进行最大值排序,然后再reduce输出时,控制只输出前n个数。就达到了TopN输出的目的。
4、如何使用MapReduce实现两表的join?
-
reduce side join:在map阶段,map函数同时读取两个文件File1和File2,为了区分两种来源key/value数据对,没条数据打一个标签(tag),比如:tag=0表示来自文件File1,tag=2表示来自文件File2。
-
map side join:map side join 是针对一下场景进行的优化。两个待连接的表中,有一个表非常大,而另一个非常小,以至于小表可以直接存放到内存中。这样,我们可以将小表复制多份,让每一个map task内存中存在一份(比如放在hash table中),然后只扫描大表:对于大表中的每一条记录key/value,在hash table中查找是否具有相同key的记录,入股有,则连接后输出即可。
5、MapReduce中是如何定义并行度的?
一个job的map阶段并行度由客户端提交的job决定。客户端对map阶段并行度的规划逻辑为:
将待处理数据执行逻辑切片。按照一个特定的切片大小,将待处理数据划分成逻辑上的多个split,然后每一个split分配一个maptask实例,并进行处理。
Reducetask并行度同样影响整个job的执行并发度和执行效率,与maptask的并发度由切片数决定不同,Reducetask 数据的决定是可以直接手动设置:job.setNumReduceTask(4)。