MapReduce基本框架
MapReduce计算模型的两个重要阶段:
- Map:映射,将数据转化为键值对的形式。切片操作在集群中并发执行,切片大小默认取最大切片、最小切片、块大小(128M)中间值。
- Reduce:规约,负责数据的计算合并。
shuffle作为Map和Reduce两端的衔接,是MapReduce的核心,分布在MapReduce的Mapper端和Reducer端,主要包含以下几个阶段:
Collect阶段
将Map任务输出的“键-值对”数据存储到内存中的环形缓冲区。等待缓冲区满到一定阈值(如80%)触发Spill,将数据转存到磁盘。
在环形缓冲区内,设置了索引数据和Partition分区信息。数据区域和索引区域在缓冲区中是相邻不重叠的两个区域,通常使用一个分界点来划分。数据的存储方式是向上增长,索引数据的存储方式是向下增长。初始的分界点是0,之后在每次Spill之后都会更新一次,重定位到空闲区域的中间位置。
分区规则是对key进行哈希(或者随机)取模分区,分区数通常为reduce的个数。
Sort阶段:将缓冲区的数据按照Partition值和Key关键字进行排序,只需要移动索引数据。排序结果是缓冲区中的数据按照Partition为单位聚集在一起,同一Partition内的数据按照Key有序。
Combiner阶段
可选的,分区内部相同key数据的合并或处理,如词频统计或者求最大值,来减少拷贝到reduce的文件,主要的性能提升在于网络开销
Spill阶段
根据排序后的索引,将缓冲区的Partition逐个转存到文件中。为了定位每个Partion在文件中的位置,设置三元组(起始位置、原始数据长度、压缩数据长度)索引存储到索引文件。
Merge阶段
将所有Spill出来的临时文件(数据和索引)进行聚合(我理解就是归并排序)。此时Map任务已经结束,内存已有空闲。
Copy阶段
Reduce任务通过HTTP到已经完成Map任务的节点上拷贝数据。默认保存在内存的缓冲区,当内存的缓冲区达到一定的阈值时,就会将数据写到磁盘上。
Merge Sort阶段
Merge操作通常是跟Copy操作同时进行,在后台开启两个线程对内存到本地的数据文件进行合并排序操作。分组默认是将相同的key的value放在一起。作用是为了reduce函数更好的计算相同key值出现的次数。
相关的问题理解
Mapper和Reducer的数量
Mapper的数量通常由切片数量决定,与文件大小和数量,以及切片大小有关,不能人为设定;而Reduce的数量可以自己设置。
数据倾斜
数据倾斜是由于partition分区时会将相同键值的数据分配到同一个partition,导致存在部分reduce需要处理非常多的数据。
解决方案是根据实际数据的特点自定义partition分区的规则。
MapReduce优化思路
- 小文件事先合并成大文件,减少“边角料”式的切片,充分利用计算资源(可能是HDFS的操作,切片时候需要注意的
- 环形缓冲区批量收集map结果,可以减少磁盘IO,可以增大缓冲区容量或者阈值。
- 对Map输出的数据进行压缩,减少网络传输和磁盘IO带来的性能损耗
- 避免Reduce端出现数据倾斜,自定义partition规则;
- 自定义conbiner,及时归并spill出来的文件;
- 集群优化的核心思路是减少磁盘IO和网络读写(在实际经验中分布式系统运行的瓶颈一般都是IO而不是CPU),例如、对Map输出的数据进行压缩等。
Combiner和Reduce的区别
combiner发生在map端,处理一个任务中的文件数据,不能跨map任务;reduce可以接收多个map任务进行处理。
Shuffle是必需的吗?
不是的,只有在需要去重、聚合、排序、统计等操作时。不然的话可以不要(如求平均,降低计算和磁盘的性能损耗。