Shuffle流程

Shuffle 概念:

map阶段的输出经过shuffle(分区、排序、合并)后形成一定有规则的数据,按照分区分配给对应的reduce,它描述着数据从map阶段流入reduce阶段的过程

Shuffle 过程:

shuffle过程图

整体流程如下:

  1. map结果写入环形内存缓冲区,当内存不足以存储所有数据时,将数据批量**溢写**到磁盘。为了尽量减少IO消耗,所以在数据写入磁盘之前会先写入缓冲区,待缓冲区达到==阈值(0.8)==后才批量将数据写入磁盘。

  2. partition分区。在数据写入磁盘之前会先进行分区,一个分区对应一个reducer,期望数据在多个reducer之间达到均衡。

  3. 排序(sort)和合并(combine)。数据经过分区之后,先按照key进行排序,如果用户指定了Combiner,再进行combine操作。

  4. 溢写(spill)。经过排序和合并之后的数据会写入磁盘文件,每次spill都会产生一个文件。一个分区上的文件也叫一个segment。

  5. 归并(merge)。一个map最终会生成一个磁盘文件,由于多次spill会产生多个文件,所以需要将这些文件进行merge,最终形成一个有序的大文件。merge过程中有可能遇到相同key的数据,如果用户设置了Combiner,会执行combine操作。

    以上1-5是map阶段的shuffle,以下是reduce阶段的shuffle步骤

  6. 拷贝(copy)。当某个map完成后,reduce不断拉取map生成的文件到ruduce。和map阶段一样先将数据写入环形内存缓冲区,当达到阈值时,将数据批量溢写到磁盘。

  7. 排序(sort)和归并(merge)。sort是伴随copy动作时执行的,由于map的输出是有序的,所以copy是进行sort消耗很低。当溢写数据到磁盘之前,如果用户设置了Combiner会先进行combine,然后将数据写入磁盘文件。当接受完map数据会生成多个溢写磁盘文件,将这些文件归并merge,合并成一个有序的大文件。

map阶段shuffle详细过程

map阶段shuffle过程就是将map结果进行分区、排序、合并,然后将同一分区数据归并到一起写入磁盘,最终生成一个分区有序的大文件。

1、写入内存环形缓冲区
map的运算结果会先写入内存缓冲区,当数据量不超过内存大小时,数据就一直在内存中保存,直到所有数据写入完毕,再一次性将数据写入磁盘文件。缓冲区默认100M,由参数mapreduce.task.io.sort.mb控制,当达到默认阈值0.8(80%)时,会触发溢写磁盘动作,阈值由参数mapreduce.map.sort.spill.percent控制。

注意:

  1. 如果单条记录超过缓冲区大小将直接触发溢写
  2. 缓冲区不是阻塞的。当达到阈值进行写入磁盘时,往缓冲区的写入也是正常进行的,写入磁盘完成后,缓冲区达到阈值时会再次进行写入。

2、Partition(分区)
在内存环形缓冲区达到阈值需要写入磁盘之前,会对数据按照key进行分区,每个分区数据对应一个reduce,从而达到数据均衡的目的。
分区可能会导致数据倾斜问题,即某个reduce可能会获取大量数据,某个reduce很少甚至没有数据。因此官方提供了几个分区器Partitioner,有默认的HashPartitioner,也有BinaryPartitionerKeyFieldBasedPartitionerTotalOrderPartitioner,用户也可以继承Partitioner基类自定义分区逻辑,通过job.setPartitionerClass()设置不同的Partitioner,避免数据倾斜问题。

官方内置HashPartitioner:

public class HashPartitioner<K2, V2> implements Partitioner<K2, V2> {
	public void configure(JobConf job) {}
	/** Use {@link Object#hashCode()} to partition. */
	public int getPartition(K2 key, V2 value,
                            int numReduceTasks) {
		return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
	}
}

3、Sort & Combine(排序&合并)
当触发spill动作后,在写入磁盘文件之前按照partition和key进行排序,保证一个partition内的数据有序。如果用户通过job.setCombinerClass()设置了Combiner,则会将相同key的数据执行combine操作。

注意:

  1. 用户自定义Combiner需要继承Reducer类,重写reduce方法
  2. Combiner虽然会一定程度上将数据合并,减小IO消耗,但是由于相同key的数据合并后,可能会导致出现非预期计算结果

4、Spill(溢写)
当发生溢写时启动一个独立的spill线程,负责将数据按照分区逐个写入磁盘文件。写入文件时先按照一个分区数据顺序写入,写完一个分区数据再继续写入下一个分区数据,直到所有分区数据写入完成,最终所有分区数据都按照分区有序的存储在同一个文件中。
一个partition中的数据也叫一个segment,这些分区和数据信息统一存由一个元数据索引维护。

5、Merge(归并)
如果map产生的结果数据量很大,会发生多次spill,生成多个磁盘文件,此时就需要将这些文件进行合并,最终生成一个有序的大文件。
合并时候是按照partition一个一个进行合并的,首先从索引中查出这个partition对应的segment,然后归并合并这些文件数据,当一个分区合并完后再执行下一个分区的合并。
mapreduce.task.io.soft.factor参数控制merge过程同时打开的segment数量。默认是10,可按照实际情况调整优化Job。

reduce阶段shuffle详细过程

当map彻底完成之后会通知NodeManager,NodeManager会向ApplicationMaster发送心跳汇报信息,reduce从ApplicationMaster获取map完成信息后,向对应的NodeManager发送请求,开始启动reduce的Shuffle,循环拉取map数据(copy),最后将多个文件合并成一个有序的大文件。

6、Copy
当reduce获知map输入文件所在节点后,建立HTTP连接,不断拉取数据到reduce的内存缓冲区中。reduce阶段shuflle中的内存缓冲区和map阶段的不同,缓冲区内存使用heap size通过参数mapreduce.reduce.shuffle.input.buffer.percent控制,它是相对于mapreduce.reduce.java.opts-参数值得百分比。
mapreduce.reduce.shuffle.parallelcopies参数控制copy的线程数量,用户可根据实际情况进行Job调优。

7、Sort & Merge
从map读取过来的数据先写入内存缓冲区中,reduce中shuffle的内存缓冲区通常比map阶段的大很多,它使用的heap size。当缓冲区达到一定阈值后,开始内存到磁盘的merge,触发参数由mapreduce.reduce.shuffle.merge.percent控制。sort是伴随copy执行的,由于map的输出结果是有序的,因此在copy时顺便将数据sort。在写入磁盘前,如果用户设置了Combiner会先执行combine操作。当map所有数据都读取到reduce中时,会在生成多个磁盘文件,此时需要将这多个文件合并成一个有序的大文件,叫做磁盘merge。
mapreduce.task.io.soft.factor参数控制文件句柄打开数量,影响merge的速度。

当reduce的shuffle执行完成后,才开始真正执行Reducer任务。由于shuffle完成后所有输入都写入了磁盘,所以reducer执行时需要从磁盘拉取数据进行计算。mapreduce.reduce.input.buffer.percent参数控制reducer是否使用内存计算,默认0不使用内存计算。可以通过调整此参数增加reducer计算速度

shuffle调整参数

参数描述默认值默认值
mapreduce.task.io.sort.mbmap阶段缓冲区大小100M
mapreduce.map.sort.spill.percentmap缓冲区溢写到磁盘的触发阈值0.8
mapreduce.task.io.sort.factormerge时打开的文件句柄数,也叫文件流数10
mapreduce.map.output.compress是否开启map压缩false
mapreduce.map.output.compress.codec压缩格式org.apache.hadoop.io.compress.DefaultCodec
mapreduce.reduce.shuffle.parallelcopiesreduce阶段shuffle从map结果copy数据并行度5
mapreduce.reduce.shuffle.input.buffer.percentreduce阶段shuffle的缓冲区大小,相对于mapreduce.reduce.java.opts参数值百分比0.7
mapreduce.reduce.shuffle.merge.percentreduce缓冲区溢写到磁盘的触发阈值0.66
mapreduce.reduce.merge.inmem.thresholds从map读取多少条记录后触发内存到磁盘溢写int类型,0表示禁用
mapreduce.reduce.input.buffer.percentreduce期间用来缓存map结果大小,堆内存百分比。当shuflle结束后,内存中剩余数据必须小于此值才开始reduce计算0.0
  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值