MapReduce的详细工作流程

3. MapReduceTask工作流程

整理自Hadoop技术内幕

3.1 概述

MapReduce框架中,一个Task被分为Map和Reduce两个阶段,每个MapTask处理数据集合中的一个split并将产生的数据溢写入本地磁盘;而每个ReduceTask远程通过HTTP以pull的方式拉取相应的中间数据文件,经过合并计算后将结果写入HDFS。

3.2 MapTask

在这里插入图片描述

客户端提交任务,规划切片,并将切片文件,程序jar包与任务配置文件上传,Hadoop集群分配节点执行任务;

  • Read阶段:MapTask按InputFormat中定义的方法将数据读取如内存;

  • Map阶段:将内存中的数据按map()函数定义的方法处理成K-V对的形式;

  • Collect阶段:将K-V对的数据与索引向两个方向写入环形缓冲区(默认100M大小);

    • 调用Partitioner.getPartition()获取记录的分区号,再将<key,value,partition>传给MapOutputBuffer.collect()做进一步处理;

    • MapOutputBuffer内部使用了一个缓冲区暂时存储用户输出数据。几种不同的缓冲区优劣:

      • 单向缓冲区:生产者像缓冲区中单向写,写满后一次性写磁盘。性能低,不能同时读写数据。
      • 双缓冲区:一个用于写入数据,一个用于溢写磁盘,交替读写。仍会存在读写等待问题。
      • 环形缓冲区:缓冲区使用率达到一定阈值后便开始溢写磁盘,同时生产者仍可以像不断增加的剩余空间中循环写入数据,读写并行。
    • 环形缓冲区实际是通过一个线性缓冲区模拟,通过取模操作实现循环取数据。缓冲区的读写采用的是典型的但生产者消费者模型。

    • Hadoop2.x让kvmeta、kvbuffer共用一个环形缓冲区,Hadoop0.22之前是采用了两级索引结构,涉及三个缓冲区,分别为kvoffsets、kvindices、kvbuffer。

    • 实际实现中元数据信息是从末尾到头的顺序写入数组,元数据16byte,包括index、partition、keystart、valuestart。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-89LnSfA4-1586451386183)(picture\kv.png)]

  • Spill阶段:缓冲区内存占用到80%(默认)后溢写入本地磁盘;

    • 溢写入磁盘之前会对缓冲区中的数据先按照分区编号Partition再按key进行排序 ,使用的是Hadoop自己实现的快速排序,Hadoop中快速排序的优化体现在:

      (1)枢轴选择:中位数中元素作为枢轴;

      (2)子序列划分:左右两个扫描的索引遇到元素大于等于/小于等于枢轴元素时才会停止;

      (3)对相同元素的优化:将序列化分为三部分,中间部分为与枢轴相同的元素,不参与后续的递归;

      (4)减少递归次序:子序列中元素数目小于13时使用插入排序,不再继续递归。

    • 按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文件output/spillN.out(N表示当前溢写次数)中。如果用户设置了Combiner,则写入文件之前,对每个分区中的数据进行一次聚集操作。

    • 将分区数据的元信息写到内存索引数据结构SpillRecord中,其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大小超过1MB,则将内存索引写到文件output/spillN.out.index中。

    • 溢写同时仍然可以写入数据。

  • Combiner阶段:当所有数据处理完成后,MapTask对所有临时文件进行合并、排序,最后只会生成一个数据文件

    • 按分区合并文件,对于某个分区,采用多轮归并的方式排序合并;
    • 每轮合并100(默认)个文件,并将新文件加入待合并列表中,知道得到最后一个大文件。MapTask的输出结果保存为IFile的格式(支持按行压缩,可用Zlib、BZip2等压缩算法减小IO数据量)
3.3 ReduceTask

ReduceTask从各个MapTask上读取数据,排序后以组为单位交给reduce()函数处理并将结果写入HDFS。
在这里插入图片描述

  • Shuffle阶段:ReduceTask从各个MapTask上拷贝一片数据,针对每一片数据,如果其大小超过阈值,则写入磁盘,否则直接放在内存中。Map阶段完成至少进行5%以后开始拷贝。

  • Merge阶段:拷贝远程数据的同时,ReduceTask启动两个后台线程对内存和磁盘上的文件合并。(归并排序)

    Shuffle和Merge过程可以分为三个子阶段:

    • GetMapEventsThread线程周期性(心跳间隔)通过RPC从TaskTracker获取已完成的MapTask列表。
    • ReduceTask同时启动多个MapOutputCopier线程(默认5个),通过HTTP Get远程拷贝数据,存入内存或磁盘中。
    • ReduceTask启动LocalFSMerger和InMemFSMergeThread两个线程进行Merge。
    • 当内存中的数据已占有超过66%的堆内存、或内存中文件数超过1000个或阻塞在ShuffleRamManager上的请求数大于拷贝线程数的75%时,内存中的数据会溢写入磁盘。
  • Sort阶段:Merge阶段已经减少了文件数目,这阶段只需对所有数据进行一次归并排序即可。

  • Reduce阶段:按key将数据传入reduce()函数中聚合。

    • Sort阶段与Reduce阶段也是并行的。在Sort阶段,ReduceTask为内存和磁盘中的文件建立了小顶堆,保存了指向根节点的迭代器。ReduceTask不断移动迭代器,以将相同key的数据顺次交给reduce()函数处理。移动迭代器的过程其实就是不断调整小顶堆的过程。
    • 这两个阶段必须在数据全部拷贝完后才能进行,是一个性能瓶颈。
  • Write阶段:将计算结果写入HDFS。

3.4 MapReduce的优化
  • 参数调优:

    • 可设置环形缓冲区大小、溢写阈值大小、是否压缩、压缩器选择、ReduceTask同时启动的拷贝线程数、Reduce阶段的内存阈值、合并阈值等。
    • 合理增加Combiner,减小IO

    杜克大学Starfish项目:自动参数调优

  • 系统调优:

    • 避免排序;
    • Shuffle阶段内部优化:Netty代替Jetty(Hadoop2.X)、Reduce端批拷贝、Shuffle阶段独立出来以分离IO与CPU的资源重叠使用;
    • C++改写;
3.5 MapReduce的一些应用
3.5.1 TopN

数据保存入一个小顶堆(可用Reduce端放置一个TreeMap实现)

3.5.2 join

MapJoin:驱动中加入缓存,重新setup阶段保存小表,性能更优

ReduceJoin:连接键作为key,性能较差

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值