MapReduce小结

相关:
HDFS小结
参考资料
什么是MapReduce,MapReduce的工作流程和原理是什么
mapreduce中split划分分析(新版api)
mr!shuffle详细全过程

MapReduce概况

  MapReduce是谷歌提出的一种分布式计算框架,用于大规模数据集的并行运算。MapReduce更像是一种思想,而框架只是这种思想的实现。MapReduce将数据的处理过程提炼成两个足够通用的步骤,即Map和Reduce,通过使用代码自定义这两部分,MapReduce几乎可以处理所有形式的需求。MapReduce的设计理念可以简单的总结为移动计算而不是移动数据。HDFS给出了大规模数据存储的解决方案,MapReduce与HDFS承接,将“计算过程”分布至各个数据,再将计算后的结果结合起来。
在这里插入图片描述fig.1

在这里插入图片描述fig.2
  上图是一张经典MapReduce统计词频的过程图,MapReduce的过程可以分为:

  1. Split 将数据分割,即使HDFS中已经有了block的概念,但在MapReduce中仍然需要分割,原因是block的大小是固定的,并不一定适用于所有的场景,且Map步骤需要接收键值对形式的数据。
  2. Map split后的每一个块都会对应一个Map线程,Map部分的功能可以编写代码自由定义,接收形式为键值对,输出形式也为键值对。
  3. Shuffle 把Map的输出按照某种key值重新切分和组合成n份,把key值符合某种范围的输入到特定的reduce去,Shuffle的作用是承上启下。
  4. Reduce Reduce就是将Shuffle后的结果进一步合并,产生最终结果。Reduce的个数由代码决定与数据片段并不一一对应,可以人为使用代码设置。

Split

  首先看一下split的size的规则,在split之前会设置max.split和min.split,决定分割的最大size和最小size,最终分割size的定义公式为:
max ⁡ (  min.split, min(max.split, block)  ) \max (\text { min.split, min(max.split, block) }) max( min.split, min(max.split, block) )
  其含义为取max.split min.split block_size的中间数。

  划分的逻辑如下:

  1. 遍历输入目录中的每个文件,拿到该文件;
  2. 计算文件长度,两种情况,A:如果文件长度为0,如果mapred.split.zero.file.skip=true(是否跳过0文件),则不划分split;如果mapred.split.zero.file.skip为false,生成一个length=0的split。B:如果长度不为0,跳到步骤3;
  3. 判断该文件是否支持split :如果支持,跳到步骤4;如果不支持,该文件不切分,生成1个split,split的length等于文件长度;
  4. 根据当前文件,计算splitsize,假设计算结果为100M;
  5. 判断剩余待切分文件大小/splitsize是否大于SPLIT_SLOP(可以理解为文件最少能切多少份,该值代码中写死)如果true,切分成一个split,待切分文件大小更新为当前值-splitsize ,再次切分,生成的split的length等于splitsize,循环往复; 如果false ,那么直接将剩余的切到一个split里,生成的split length等于剩余待切分的文件大小。之所以需要判断剩余待切分文件大小/splitsize,主要是为了避免过多的小的split。比如文件中有100个109M大小的文件,如果splitSize=100M,如果不判断剩余待切分文件大小/splitsize,将会生成200个split,其中100个split的size为100M,而其中100个只有9M,存在100个过小的split。MapReduce首选的是处理大文件,过多的小split会影响性能。

  切割完之后会将结果处理成键值对输出给Map,何为键何位值,针对不同类型的数据有不同的处理方式,对应了不同的接口。其实这一步输出的键-值没必要过于深究,它只是为了符合Map的输入格式,无论何为键何为值,只要原始数据没有遗漏,理论上就不影响Map的使用。

Map

  Map步骤接收一个键值对,输出一个键值对,两个键值对转换的过程就是就是需要根据业务去编程的部分。Map对应到每一个split后的块,每一个块都会对应一个Map线程,他们并行运算。

Shuffle

  这一步大概是最复杂的一步,这一步中包含了可由开发人员定义的部分,也有不包含定义的部分,不仅是内存中的运算,还存在IO交互。总结下来shuffle可以分为三步:partition、sort&combiner、spill to disk。

partition

  partition就是分区了,分布式里真是有各种分,block、split、partition,但其实各有用处。这里的分区是为了后续reduce方便,同一个分区的所有数据,会在最后合并后分给一个reduce处理。简单的例子就是图2中的统计词频,显然将同样的单词分到同一个区,最后就可以利用reduce计算出这个单词的词频。所以这里的关键就是——分区规则是什么。

  分区规则当然是可以通过代码自定义的,但如果不定义的情况下,它也有默认规则:哈希模运算。前面说过Map的输出是key-value,找到key的哈希值,对reduce的个数(可设定)做模运算,运算结果就是这条数据分区。这里的分区并不是移动数据,而是给数据进行一个标记。

  分区除了方便类似例子中词频统计业务上的需求,大多数情况下也为了解决数据倾斜和负载均衡问题,即平衡每个reduce的工作压力。

sort&combiner

  这一步分为两个步骤:排序和合并,都是对一个partition内数据的整理。

  sort规则是可以自定义的,即可以编写代码决定利用什么进行排序,默认规则为key值的ascii码。

  combiner可有可无,如果不定义那就没有,也没有默认规则。combiner有其特定的适用场景,例如图2中的词频统计,在shuffle阶段,可以将partition内的数据提前进行一波小合并,计算出每个单词在自己partition内的数量,这样减少了reduce的输入数据,方便了后续reduce的计算。

spill to disk

  Map阶段的计算是在内存中进行的,其结果会存在一个内存缓冲区中。但内存是有限的,数据量比内存的大的情况非常常见,所以当存储结果的内存缓冲区满了的时候,就需要将结果写到磁盘中。这一步中也有一个合并操作,称为merge,作用是将持续写到磁盘中的文件合并成一个大文件。

  至此就是Shuffle在Map端的整个流程,整个流程全部在Map task节点完成,不会进行跨网络取数据。前面说过Shuffle是一个承上启下的步骤,所以它在Reduce端还会有一部工作。

Reduce

在这里插入图片描述fig.3

  Reduce端的Shuffle如图3所示。前面说过Reduce按照分区处理数据,每个Reduce会处理1个或多个分区的数据,所以第一步就是从不同的Map结果中把所需分区的数据copy过来,这里走的是网络交互。

  从不同Map结果copy过来的数据传会依次进入内存缓冲区中,如果直至copy完毕内存也没爆,则数据在内存中就可进行合并,然后送至Reduce中,此为内存到内存merge。

  如果在copy过程中内存满了,那么类似Shuffle前面的步骤,会将内存中的数据merge至磁盘的一个文件中,此为内存至磁盘merge。

  当属于该reducer的map输出全部拷贝完成,则会在reducer上生成多个文件,这时开始执行合并操作,即磁盘到磁盘merge,Map的输出数据已经是有序的,Merge进行一次合并排序,所谓Reduce端的sort过程就是这个合并的过程,采取的排序方法跟map阶段一样,针对键进行排序。一般Reduce是一边copy一边sort,即copy和sort两个阶段是重叠而不是完全分开的。

  Reduce端Shuffle的merge,都是根据key值进行merge,且无法进行自定义。最终Reduce shuffle过程会输出一个整体有序的数据块,不同key的数据依次进入Reduce计算,最终得到结果。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值