大数据-Hadoop-MR

MR介绍

MapReduce 是 Google 推广的一个简单的编程模型,它对以高度并行和可扩展的方式处理大数据集很有用。 MapReduce 的灵感来源于函数式编程,用户可将他们的计算表达为 map 和 reduce 函数,将数据作为键值对来处理。 Hadoop 提供了一个高级 API 来在各种语言中实现自定义的 map 和 reduce 函数。

Hadoop 基础架构负责处理分布式处理的所有复杂方面:并行化、调度、资源管理、机器间通信、软件和硬件故障处理等等。 得益于这种干净的抽象,实现处理数百(或者甚至数千)个机器上的数 TB 数据的分布式应用程序从未像现在这么容易过,甚至对于之前没有使用分布式系统的经验的开发人员也是如此。

MR执行前检查

MR提交到集群后,会做如下操作:

  1. 检查作业输出说明
  2. 计算作业输出划分Split
  3. 将作业所需要的资源(jar文件,配置文件,计算所得的输入划分)复制到集群中

备注:

  • 关于Step 1:
          判断输出文件是否存在的目的是为了保证数据的完整性. Hadoop设计思想认为: job都是庞大且耗时的, 如果能在执行前检查到错误并结束,会节省很多时间
  • 关于Step 2:  计算 Split其实就是计算 Map 数

MR架构

MR将任务分割为 Map 端和 Reduce 端. 下图就是 Map/Reduce 过程图:

Map端

  1. 读取HDFS
  2. Split(见'Split(分割)')
  3. Map运算-具体业务
  4. Shuffle(见'Map端的Shuffle')
  5. Map端的所有工作都已结束, 最终生成的这个文件也存放在TaskTracker够得着的某个本地目录内
  6. 通知 JobTracker Task完成

    当你想更改map的个数的时候:
          1). 通过更改配置文件中block的size来增大或者减小map的个数
          2). 通过 JobConf's conf.setNumMapTasks(int num)

           但是就算你设置了数目在这里,它在实际运行中的数目不会小于它实际分割产生的数目。意思就是当你通过程序设置map为2个,但是在读入数据的时候,分割数据是需要3个,那么最后任务在实际运行的过程中map个数是3个而不是你设置的2个

     Map的个数是由任务本身的数据量决定的

Reduce端

  1. Shuffle(见'Reduce端的Shuffle')
  2. Reducer 处理输入,写入 HDFS
  3. Reducer去Mapper中拉取文件
  4. Mapper会Merge出结果文件,直到文件生成之前,Reducer无法拉取
  5. 多个Mapper间的文件是可以被Reducer并行拉取的

    当你想修改reduce的个数那么可以按照如下方法进行更改:
          1). 在conf设置中调用conf.setStrings("mapred.reduce.tasks", values)
         2). 调用job.setNumReduceTasks(tasks)

     Reduce的个数hadoop是默认设置为1的,因为一个任务的输出的文件个数是由reduce的个数来决定的

总结:

  • Split 决定Map数
  • Shuffle 过程横跨 Map 和 Reduce 两端
  • 排序与合并过程横跨 Map 和 Reduce 两端
  • Map的个数为动态, Reduce的个数默认为1
  • 很多人的误解如果不使用 Combiner便不会排序,这是错误的,不管你用不用 Combiner,Map Task 均会对产生的数据排序(如果没有 Reduce Task,则不会排序,实际上 Map 阶段的排序就是为了减轻 Reduce端排序负载)
  • 在hadoop 1.x 中排序无法避免,也不可以关闭,但 hadoop2.x 是可以关闭的
  • 据说: 排序是影响性能的要素!
  • 据说: IO是影响性能的要素! IO确实不少且无法避免

Split(分割)

     在执行mapreduce之前,原始数据被分割成若干split,每个split作为一个map任务的输入

     FileInputFormat: 所有以文件作为数据源的InputFormat实现的基类,它有2个功能:

  •         List<InputSplit> getSplits(): 获取由输入文件计算出输入分片,解决数据或文件分割成片问题
  •         RecordReader<K,V> createRecordReader(): 创建RecordReader,从InputSplit中读取数据

     SplitSize计算:

           splitSize = max(minsize,min(maxSize,blockSize)) = 64M;

           maxSize = mapred.max.split.size 默认最大值整数值

           minSize = mapred.min.split.size 默认0

            通过公式可以看出:

  •                   FileInputFormat只划分比HDFS block大的文件
  •                  如果一个文件的大小比block小,将不会被划分
  •                  例如: 一个1G的文件, 会被划分成16个64MB的split,并分配16个map任务处理
  •                  例如: 10000个100kb的文件会被10000个map任务处理

    为什么默认分片大小与分块大小是相同的原因??

           hadoop在存储有输入数据(HDFS中的数据)的节点上运行map任务,可以获得高性能,这就是所谓的数据本地化。所以最佳分片的大小应该与HDFS上的块大小一样,因为如果分片跨越2个数据块,对于任何一个HDFS节点(基本不肯能同时存储这2个数据块),分片中的另外一块数据就需要通过网络传输到map任务节点,与使用本地数据运行map任务相比,效率则更低

    小文件问题:
           如果一个文件的大小比block小,将不会被划分,所以每一个小文件都会被当做一个split并分配一个map任务,这也是Hadoop处理大文件的效率要比处理很多小文件的效率高的原因(10000个100kb的文件会被10000个map任务处理,肯定排队呀!

Shuffle

      Shuffle描述着数据从map task输出到reduce task输入的这段过程

      Shuffle 过程横跨 Map 和 Reduce 两端

      在Hadoop这样的集群环境中,大部分map task与reduce task的执行是在不同的节点上。 当然很多情况下Reduce执行时需要跨节点去拉取其它节点上的map task结果。 如果集群正在运行的job有很多,那么task的正常执行对集群内部的网络资源消耗会很严重。 这种网络消耗是正常的,我们不能限制,能做的就是最大化地减少不必要的消耗。 还有在节点内,相比于内存,磁盘IO对job完成时间的影响也是可观的。从最基本的要求来说, 我们对Shuffle过程的期望可以有:

  1. 完整地从map task端拉取数据到reduce 端
  2. 在跨节点拉取数据时,尽可能地减少对带宽的不必要消耗
  3. 减少磁盘IO对task执行的影响

Map端的Shuffle

先看看map端的情况, 通过下图希望让大家清晰地了解从map数据输入到map端所有数据准备好的全过程:

下面我对细节来一一说明(注意红色圆数字):

  1. 步骤1(输入/Split):
           在map task执行时,它的输入数据来源于HDFS的block,当然在MapReduce概念中,map task只读取split。Split与block的对应关系可能是多对一,默认是一对一。
  2. 步骤2(Partition):

          通过MapReduce提供的Partitioner接口, Mapper 输出映射到 Reducer 上

         确定 Reducer 后, 将数据写入'内存缓冲区'中,缓冲区的作用是批量收集map结果,减少磁盘IO的影响

          ***写入之前,key 与value 值都会被序列化成字节数组

  3. 步骤3(Spill, Sort, Combiner):

          本步骤会发生Spill, Sort, Combiner. 具体请见相应的部分~~~

          当map task输出结果很多时。Map会将数据写入磁盘,保证缓冲区可以重复使用. 这个叫Spill(具体见'Spill')

          当Spill时, MR程序默认会对写入内容按key排序, 然后合并(Combiner), 详情见'Combiner'

  4. 步骤4(Merge):

          每次溢写会在磁盘上生成一个溢写文件, 如果map的输出结果真的很大就会生成多个溢写文件

          当map task真正完成时,内存缓冲区中的数据也全部溢写到磁盘中形成一个溢写文件

          故当map task真正完成时,会有一个或多个溢写文件,所以需要将这些溢写文件归并到一起,这个过程就叫做Merge

          Merge过程中如果client设置过Combiner, 也会使用Combiner来合并相同的key

总结:

  • Mapper 输出映射到 Reducer发生在 Partition 过程中
  • Mapper 输出写入缓冲区时,未进行排序与合并
  • 排序与合并发生在 Spill与Merge 两个阶段
  • 序列化发生在 Partition 阶段
  • 内存缓冲区中的数据最终要生成文件,这也是Hadoop采用文件中转的原因. 区别于Spark

Reduce端的Shuffle

通过下图描述Reduce端的Shuffle细节:

下面我对细节来一一说明(注意红色圆数字):

  1. 步骤1(Copy):
           简单地拉取数据. Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求map task所在的TaskTracker获取map task的输出文件。
           因为maptask早已结束,这些文件就归TaskTracker管理在本地磁盘中。
  2. 步骤2(Merge):
           Copy 过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比 map 端的更为灵活,它基于 JVM 的 heap size 设置,因为    Shuffle 阶段 Reducer 不运行,所以应该把绝大部分的内存都给 Shuffle 用。

          Merge 有三种形式:

  •                     内存到内存: 默认情况下不启用,让人比较困惑
  •                     内存到磁盘: 当内存中的数据量到达一定阈值就会写入磁盘. 与Map端Spill类似,也会排序和合并
  •                     磁盘到磁盘: 当Map端数据读取完成后. 启动磁盘到磁盘的Merge方式生成Reducer输入文件
  1. 步骤3(Reducer的输入):
           Merge 的最后会生成一个文件,大多数情况下存在于磁盘中,但是需要将其放入内存中。 当reducer 输入文件已定,整个 Shuffle 阶段才算结束。然后就是 Reducer 执行,把结果放到 HDFS 上。

总结:

  • Reducer真正运行之前,所有的时间都是在拉取数据,做Merge
  • 在 Merge 过程中会发生排序与合并,包括 '内存到磁盘' 与 '磁盘到磁盘'

分区(Partition)

       Mapper后的结果集该传递给哪个reduce去做下一步操作, 如何决定??? 答案就是通过MapReduce提供的Partitioner接口

       它的作用就是根据key或value及reduce的数量来决定当前的输出数据最终应该交由哪个reduce task处理

       Partition是shuffle的一部分

默认规则如下:
       对key进行hash后,除以reduce task数量取模, 取模方式只是为了平均reduce的处理能力

       如果用户自己对Partitioner有需求,可以订制并设置到job上

溢写(Spill)

       当map task输出结果很多时就可能发生内存溢出,所以需要在一定条件下将缓冲区的数据临时写入磁盘,然后重新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为 Spill,中文可译为溢写

        溢写是由单独线程来完成,不影响往缓冲区写map结果的线程

       整个缓冲区有个溢写的比例(spill.percent), 这个比例 默认是0.8 ,故溢写线程启动时不应该阻止map的结果输出

       当缓冲区的数据已经达到阈值时,溢写线程启动锁定这80%的内存,执行溢写过程。 Map task的输出结果还可以往剩下的20%内存中写,互不影响

合并(Combine)

       在 Spill 过程中, MR程序会将数据按key合并到一块,这个过程叫reduce也叫combine

        但MapReduce的术语中,reduce只指reduce端执行从多个map task取数据做计算的过程。除reduce外,非正式地合并数据只能算做combine了

        注意:非正式地合并叫combine,其实大家知道的,MapReduce中将Combiner等同于Reducer

  •        Combine 的作用是优化结果,减少溢写到磁盘的数据量, 所以它在整个模型中会多次使用
  •        Combiner的输出是Reducer的输入,Combiner绝不能改变最终的计算结果, Combiner的使用一定得慎重
  •        累加,最大值等Combine操作不影响最终结果, 平均值则不行~~

感激: https://blog.csdn.net/zengxiaosen/article/details/73189207 的文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值