MapReduce工作流程
map task工作流程
- Read map task通过用户编写的RecordReader,从输入InpuSplit中解析key-value
- Map 将解析的kv对交给用户编写
map()
处理,并产生一系列新的kv对 - Collect 用户编写的
map()
执行结束以后,一般会调用OutputCollector.collect()
输出结果,会将map()
生成的kv对进行分区(调用Partitioner),并写入环形内存缓冲区中。缓冲区默认大小为100M,通过mapreduce.task.io.sort.mb
控制。 - Spill 当环形缓冲区占用80%以后(
mapreduce.map.sort.spill.percent
),会首先写入临时文件中。写入线程是独立的,不影响map向缓冲区写入数据。在写入到磁盘之前,根据reduce数量划分分区。在每个分区中,后台线程会根据key进行排序。 - Combine 当所有数据处理完成后,会归并所有临时文件,生成一份最终的数据文件。
reduce task工作流程
- Copy reduce task从各个map task中远程copy对应分区的数据,如果数据不大写入内存,数据太大写入文件,通过
mapreduce.reduce.shuffle.input.buffer.percent
控制 - Merge 在copy数据的同时进行,reduce task启动线程对内存和文件中数据进行合并
- Sort 数据复制完成后,为了将相同key的数据聚合在一起,需要将所有map task的结果归并
- Reduce 执行用户函数
reduce()
在整个MapReduce过程中,主要用到快速排序和归并排序,分别发生在map阶段2次,reduce阶段1次:
- Map任务Spill写入前,各自分区根据key进行一次排序
- Map任务将多个临时文件归并为一个数据文件
- Reduce任务接收所有map task的数据后进行归并,将所有map输出合并为一个数据文件
关于sql如何转换为MapReduce过程
From操作找出输入的表或文件,这在表达式解析的过程中完成。Join操作会涉及map阶段和reduce阶段,两个表的编号作为map中value的key值,并在reduce阶段拼接两表的数据。where操作如果涉及到分区字段,会在sql解析时进行裁剪;如果不涉及分区字段,会准确设置过滤条件,并在map阶段过滤。Group by操作会作为map阶段的关键字,利用MapReduce进行排序,并在reduce端组合输出。Select操作在编译解析时直接剪裁列。Sum涉及聚合函数,并行聚合函数可以在map阶段提前聚合,减少reduce阶段网络传输。Distinct操作则直接依附在Group by字段后面进行处理。Order by操作只存在一个reducer,保证全局有序。
数据倾斜
-
读倾斜 即某个map在读取数据阶段长期无法完成。这通常是因为文件分块过大或者此分块数据有一茶馆。这种场景出现频率较小。
-
算倾斜 某个需要排序或者聚合操作的时候,同一个key的处理耗时过长。在spark框架中可以通过广播关联的方式彻底避免排序
广播关联
通过参数
spark.sql.autoBroadcastJoinThreshold
控制自动触发广播关联的大小限制。广播关联会将表的全部数据传输到各个节点的内存中,通过直接的内存操作快速完成关联。这种方式最大的好处就是避免了对主表的数据shuffle,但是会增加任务使用的内存 -
文件操作倾斜 数据生成临时文件后,由于数量巨大,重命名和移动的操作非常耗时。这通常发生在动态分区导致小文件的情况。如shuffle中执行
distinct
group by
reduce by
aggregate by
join
repartitioin
等操作时容易出现
hive.groupby.skewindata = true
使数据倾斜时负载均衡。此时生成的查询计划将会有两个MRJob。第一个MR中map的输出结果集合会随机分布到reduce中,每个reduce做部分聚合操作输出结果,这样处理的结果是相同的GroupBy key有可能分发到不同的reduce中,从而达到负载均衡的目的。第二个MR再根据预处理的数据结果按照GroupBy key分发到reduce中,最后完成聚合操作。
参考文献:
[1]: Hadoop MapReduce 一文详解MapReduce及工作机制
[2]: 最新Hadoop的面试题总结