Hadoop中MapReduce的细节

一、数据本地化策略

  1. 当JobTracker访问资源的时候需要向NameNode请求数据
  2. JobTracker获取到数据的描述信息,根据描述信息对数据进行了切片(InputSplit),然后将切片发给不同Mapper来执行
  3. MapTask在TaskTracker上执行,在执行的时候需要获取实际的数据
  4. TaskTracker需要去访问DataNode,为了节省带宽资源,所以往往将DataNode和TaskTracker放在同一个节点上 — 数据本地化策略
  5. 为了减少网络资源的消耗,往往还会将切片的大小和实际的Block的大小设置的相同

二、MapReduce的执行流程

    1. 准备阶段:

           客户端先向JobTracker申请一个job任务,JobTracker会给job任务分配一个jobid - hadoop jar xxx.jar 

  • 检查job指定的输入输出路径
  • 进行切片划分
  • 如果有必要可以设置缓存存根(现在几乎用不到)
  • 将jar包以及配置提交到HDFS上,放在tmp/hadoop-yarn/staging/jobid
  • 真正将job任务提交给JobTracker,并且可以选择监控运行状态

     2. 提交阶段  

  • JobTracker收到任务之后根据切片数量以及分区数量来确定MapTask以及ReduceTask的数量
  • JobTracker分好任务之后,等待TaskTracker的心跳,当TaskTracker心跳过来之后,那么就会给TaskTracker分配任务。注意,在分配任务的时候,MapTask要尽量满足数据本地化策略,ReduceTask分配在相对空闲的节点上
  • TaskTracker在领取到任务之后,会去连接对应的节点,将jar包下载到当前节点上 - 体现的思想:逻辑移动而数据固定
  • TaskTracker就会在本节点上开启一个JVM子进程执行MapTask或者ReduceTask。每执行一个MapTask或者ReduceTask就会去开启一次JVM子进程

三、Shuffle过程

        1. Map阶段          

  • 文件切片之后,每一个切片对应一个MapTask
  • 在MapTask中,默认按行读取,每读取一行,就调用一次map方法
  • map方法在执行的时候会将结果(这个结果中已经包含了分区信息)写到MapTask自带的缓冲区中。注意:每一个MapTask都会自带一个缓冲区
  • 当数据放到缓冲区中之后,数据在缓冲区中会进行分区(partition)、排序(sort)(扩展:在缓冲区中排序使用的排序算法是快速排序)。如果指定了合并类(combine),数据还会进行combine
  • 缓冲区是维系在内存中,默认是100M
  • 当缓冲区的使用达到指定条件(溢写阈值默认是0.8,即当缓冲区使用达到80%的时候会产生溢写)之后,MapTask会将这个缓冲区中的数据溢写(spill)到磁盘上产生溢写文件。后续的结果会继续写到缓冲区中。每一次溢写都会产生一个新的溢写文件
  • 如果产生了多个溢写文件,那么会将多个溢写文件合并(merge)成1个final out文件。如果溢写之后,后续结果放入缓冲区中但是没有达到溢写阈值,而数据又处理完成,那么MapTask会将溢写文件中的结果和缓冲区的结果直接合并(merge)到最后的final out文件中
  • 在merge过程中,结果会再次进行分区和排序,所以final out文件是整体分好区并且排好序
  • 如果指定了合并类(Combiner),并且溢写文件的个数>=3个,那么在merge过程中会自动进行一次combine
  • 注意问题                  

                        ①: 溢写不一定产生

                        ②:溢写是否与输入的切片大小没有直接的关系

                        ③:溢写文件的大小要考虑序列化的因素

                        ④:缓冲区的本质上是一个字节数组,这个字节数组在底层做了改变,使缓冲区形成了一个环形的缓冲区,设置                                  成缓冲区的目的是:为了减少寻址

                         ⑤:溢写和阙值的作用是为了减少阻塞

           2. Reduce阶段

  • 每一个ReduceTask都会启动fetch线程去MapTask中抓取当前要处理的分区的数据
  • ReduceTask会将抓取过来的数据暂时放到文件中存储,从每一个MapTask中抓取的数据都会对应一个小文件
  • ReduceTask会将这些小文件去合并(merge)成一个文件,在merge过程中,数据会进行排序 - 将局部有序变成整体有序 - merge过程中的排序使用的排序算法是归并排序
  • merge完成之后,ReduceTask会将相同的键对应的值放到一块产生一个迭代器,这个过程从称之为分组(group)
  • 每一个键调用一次reduce方法,reduce方法将结果写到HDFS上
  • 注意问题:

                        ①:默认fetch线程的数量为5

                        ②:fetch线程通过HTTP请求的方式去抓取数据

                        ③:merge因子默认为10,表示每10个小文件合成一个大文件

                        ④:ReduceTask阙值默认认为为0.05,即当有5%的MapTask执行结束,就启动ReduceTask抓取数据

                         ⑤:溢写和阙值的作用是为了减少阻塞

             3. shuffle调优

  • 调大缓冲区,实际生产环境中一般将这个值调为250~400M
  • 调大溢写阈值,可以减少和磁盘的交互但是同时增大了阻塞的概率
  • 实际生产环境中,尽量增加Combine过程
  • 可以对final out文件进行压缩。这种方案是对网络资源的一种取舍。如果网络资源紧张可以考虑这种方式
  • 增多fetch线程的数量
  • 增大merge因子 - 不建议
  • 减小ReduceTask的阈值

四、推测执行机制

  1. 如果出现了慢任务,那么这个时候MapReduce就会将这个任务复制一份出来放在其他节点上执行。两个任务一起执行,谁先执行完那么它的结果就作为最后结果,另一个没有执行完成的任务就会被kill掉
  2. 推测执行机制不适合于数据倾斜的场景

五、数据倾斜

  1. 数据本身有倾斜特性的,那么这就导致在处理过程中可能会出现有的任务处理的数据量大有的处理的数据量小,这种现象称之为数据倾斜
  2. 在实际生产环境中,绝大部分的数据倾斜也是发生在Reduce端,但是在极少部分场景下Map端也会倾斜
  3. Map端的倾斜条件:多文件输入,多个文件不可切并且多个文件的大小不均等 - Map端的数据倾斜的条件是非常清晰的,但是实际开发中Map端的数据倾斜是无法解决的

六、输入格式

  1. 在MapReduce中,输入格式的顶级父类是InputFormat,提供了2个方法:getSplits用于对数据进行切片的;createRecordReader针对数据产生输入流用于读取数据
  2. 如果不指定,默认使用的TextInputFormat,这个类继承了FileInputFormat,TextInputFormat默认读取按行数据交给MapTask来进行处理
  3. 在TextInputFormat中,利用LineRecordReader来读取数据,注意在读取数据的时候,从第二个切片开始,后续的每一个切片都是从第二行开始读取数据,这也就意味着从第二个MapTask开始,每一个MapTask应该是从当前切片的第二行开始处理,一直到下一个切片的第一行
  4. 实际生产过程中,一般是继承FileInputFormat
  5. 多源输入可以为每一个文件指定不同的输入格式也可以指定不同的Mapper类但是需要注意的是所有的Mapper的输出结果类型应该是一致的,因为最后只会有1个Reducer类

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

       

   

你可能感兴趣的:(Hadoop中MapReduce的细节)