MapReduce介绍

86 阅读4分钟

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次:

  1. Map任务Spill写入前,各自分区根据key进行一次排序
  2. Map任务将多个临时文件归并为一个数据文件
  3. 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的面试题总结