关于hadoop:Hadoop-入门笔记-十九-MapReduce并行度机制

9次阅读

共计 4645 个字符,预计需要花费 12 分钟才能阅读完成。

一. MapReduce 并行度机制

1. MapTask 并行度机制

1. 概念

MapTask 的并行度指的是 map 阶段有多少个并行的 task 独特解决工作。map 阶段的工作解决并行度,势必 影响到整个 job 的处理速度。那么,MapTask 并行实例是否越多越好呢?其并行度又是如何决定呢?

2. 原理机制

一个 MapReducejob 的 map 阶段并行度由客户端在提交 job 时决定,即客户端提交 job 之前会看待解决数据进行逻辑切片。切片实现会造成切片布局文件(job.split),每个逻辑切片最终对应启动一个 maptask。
逻辑切片机制 由 FileInputFormat 实现类的 getSplits() 办法实现。
FileInputFormat 中默认的切片机制:

  • 简略地依照文件的内容长度进行切片
  • 切片大小,默认等于 block 大小
  • 切片时不思考数据集整体,而是一一针对每一个文件独自切片
    比方待处理数据有两个文件:
    file1.txt 320M
    file2.txt 10M
    通过 FileInputFormat 的切片机制运算后,造成的切片信息如下:
    file1.txt.split1—0M~128M
    file1.txt.split2—128M~256M
    file1.txt.split3—256M~320M
    file2.txt.split1—0M~10M

    3. 相干参数 优化

    在 FileInputFormat 中,计算切片大小的逻辑:Math.max(minSize, Math.min(maxSize, blockSize));
    切片次要由这几个值来运算决定:
    minsize:默认值:1
    配置参数:mapreduce.input.fileinputformat.split.minsize
    maxsize:默认值:Long.MAXValue
    配置参数:mapreduce.input.fileinputformat.split.maxsize
    blocksize
    因而,默认状况下,split size=block size, 在 hadoop 2.x 中为 128M。
    然而,不论怎么调参数,都不能让多个小文件“划入”一个 split。
    还有个细节就是:
    当 bytesRemaining/splitSize > 1.1 不满足的话,那么最初所有残余的会作为一个切片。从而不会造成例如 129M 文件布局成两个切片的场面。

2. ReduceTask 并行度机制

reducetask 并行度同样影响整个 job 的执行并发度和执行效率,与 maptask 的并发数由切片数决定不同,Reducetask 数量的决定是能够间接手动设置:
job.setNumReduceTasks(4);
如果数据分布不平均,就有可能在 reduce 阶段产生数据歪斜。
留神:reducetask 数量并不是任意设置,还要思考业务逻辑需要,有些状况下,须要计算全局汇总后果,就只能有 1 个 reducetask。

二. MapReduce 工作流程详解

1. MapTask 工作机制

1. 流程图

2. 执行步骤

整个 Map 阶段流程大体如上图所示。简略概述:input File 通过 split 被逻辑切分为多个 split 文件,通过 Record 按行读取内容给 map(用户本人实现的)进行解决,数据被 map 解决完结之后交给 OutputCollector 收集器,对其后果 key 进行分区(默认应用 hash 分区),而后写入 buffer,每个 map task 都有一个内存缓冲区,存储着 map 的输入后果,当缓冲区快满的时候须要将缓冲区的数据以一个临时文件的形式寄存到磁盘,当整个 map task 完结后再对磁盘中这个 map task 产生的所有临时文件做合并,生成最终的正式输入文件,而后期待 reduce task 来拉数据。

具体步骤:

  • 首先,读取数据组件 InputFormat(默认 TextInputFormat)会通过 getSplits 办法对输出目录中文件进行逻辑切片布局失去 splits,有多少个 split 就对应启动多少个 MapTask。split 与 block 的对应关系默认是一对一。
  • 将输出文件切分为 splits 之后,由 RecordReader 对象(默认 LineRecordReader)进行读取,以 \n 作为分隔符,读取一行数据,返回 <key,value>。Key 示意每行首字符偏移值,value 示意这一行文本内容。
  • 读取 split 返回 <key,value>,进入用户本人继承的 Mapper 类中,执行用户重写的 map 函数。RecordReader 读取一行这里调用一次。
  • map 逻辑完之后,将 map 的每条后果通过 context.write 进行 collect 数据收集。在 collect 中,会先对其进行分区解决,默认应用 HashPartitioner。MapReduce 提供 Partitioner 接口,它的作用就是依据 key 或 value 及 reduce 的数量来决定以后的这对输入数据最终应该交由哪个 reduce task 解决。默认对 key hash 后再以 reduce task 数量取模。默认的取模形式只是为了均匀 reduce 的解决能力,如果用户本人对 Partitioner 有需要,能够订制并设置到 job 上。
  • 接下来,会将数据写入内存,内存中这片区域叫做环形缓冲区,缓冲区的作用是批量收集 map 后果,缩小磁盘 IO 的影响。咱们的 key/value 对以及 Partition 的后果都会被写入缓冲区。当然写入之前,key 与 value 值都会被序列化成字节数组。环形缓冲区其实是一个数组,数组中寄存着 key、value 的序列化数据和 key、value 的元数据信息,包含 partition、key 的起始地位、value 的起始地位以及 value 的长度。环形构造是一个抽象概念。缓冲区是有大小限度,默认是 100MB。当 map task 的输入后果很多时,就可能会撑爆内存,所以须要在肯定条件下将缓冲区中的数据长期写入磁盘,而后从新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为 Spill,中文可译为溢写。这个溢写是由独自线程来实现,不影响往缓冲区写 map 后果的线程。溢写线程启动时不应该阻止 map 的后果输入,所以整个缓冲区有个溢写的比例 spill.percent。这个比例默认是 0.8,也就是当缓冲区的数据曾经达到阈值(buffer size spill percent = 100MB 0.8 = 80MB),溢写线程启动,锁定这 80MB 的内存,执行溢写过程。Map task 的输入后果还能够往剩下的 20MB 内存中写,互不影响。
  • 当溢写线程启动后,须要对这 80MB 空间内的 key 做排序(Sort)。排序是 MapReduce 模型默认的行为,这里的排序也是对序列化的字节做的排序。如果 job 设置过 Combiner,那么当初就是应用 Combiner 的时候了。将有雷同 key 的 key/value 对的 value 加起来,缩小溢写到磁盘的数据量。Combiner 会优化 MapReduce 的两头后果,所以它在整个模型中会屡次应用。那哪些场景能力应用 Combiner 呢?从这里剖析,Combiner 的输入是 Reducer 的输出,Combiner 绝不能扭转最终的计算结果。Combiner 只应该用于那种 Reduce 的输出 key/value 与输入 key/value 类型完全一致,且不影响最终后果的场景。比方累加,最大值等。Combiner 的应用肯定得谨慎,如果用好,它对 job 执行效率有帮忙,反之会影响 reduce 的最终后果。
  • 每次溢写会在磁盘上生成一个临时文件(写之前判断是否有 combiner),如果 map 的输入后果真的很大,有屡次这样的溢写产生,磁盘上相应的就会有多个临时文件存在。当整个数据处理完结之后开始对磁盘中的临时文件进行 merge 合并,因为最终的文件只有一个,写入磁盘,并且为这个文件提供了一个索引文件,以记录每个 reduce 对应数据的偏移量。

至此 map 整个阶段完结。

2. ReduceTask 工作机制

1. 流程图

2. 执行步骤

Reduce 大抵分为 copy、sort、reduce 三个阶段,重点在前两个阶段。copy 阶段蕴含一个 eventFetcher 来获取已实现的 map 列表,由 Fetcher 线程去 copy 数据,在此过程中会启动两个 merge 线程,别离为 inMemoryMerger 和 onDiskMerger,别离将内存中的数据 merge 到磁盘和将磁盘中的数据进行 merge。待数据 copy 实现之后,copy 阶段就实现了,开始进行 sort 阶段,sort 阶段次要是执行 finalMerge 操作,纯正的 sort 阶段,实现之后就是 reduce 阶段,调用用户定义的 reduce 函数进行解决。
具体步骤:

  • Copy 阶段,简略地拉取数据。Reduce 过程启动一些数据 copy 线程(Fetcher),通过 HTTP 形式申请 maptask 获取属于本人的文件。
  • Merge 阶段。这里的 merge 如 map 端的 merge 动作,只是数组中寄存的是不同 map 端 copy 来的数值。Copy 过去的数据会先放入内存缓冲区中,这里的缓冲区大小要比 map 端的更为灵便。merge 有三种模式:内存到内存;内存到磁盘;磁盘到磁盘。默认状况下第一种模式不启用。当内存中的数据量达到肯定阈值,就启动内存到磁盘的 merge。与 map 端相似,这也是溢写的过程,这个过程中如果你设置有 Combiner,也是会启用的,而后在磁盘中生成了泛滥的溢写文件。第二种 merge 形式始终在运行,直到没有 map 端的数据时才完结,而后启动第三种磁盘到磁盘的 merge 形式生成最终的文件。
  • 对排序后的键值对调用 reduce 办法,键相等的键值对调用一次 reduce 办法,每次调用会产生零个或者多个键值对,最初把这些输入的键值对写入到 HDFS 文件中。

    二. MapReduce Shuffle 机制

    Shuffle 的本意是洗牌、混洗的意思,把 一组有规定的数据尽量打乱成无规则的数据
    而在 MapReduce 中,Shuffle 更像是洗牌的逆过程 ,指的是 将 map 端的无规则输入按指定的规定“打乱”成具备肯定规定的数据 ,以便 reduce 端接管解决。
    shuffle 是 Mapreduce 的外围,它散布在 Mapreduce 的 map 阶段和 reduce 阶段。 个别把从 Map 产生输入开始到 Reduce 获得数据作为输出之前 的过程称作 shuffle。

  1. Partition 阶段:将 MapTask 的后果输入到默认大小为 100M 的环形缓冲区,保留之前会对 key 进行分区的计算,默认 Hash 分区等。
  2. Spill 阶段:当内存中的数据量达到肯定的阀值的时候,就会将数据写入本地磁盘,在将数据写入磁盘之前须要对数据进行一次排序的操作,如果配置了 combiner,还会将有雷同分区号和 key 的数据进行排序。
  3. Merge 阶段:把所有溢出的临时文件进行一次合并操作,以确保一个 MapTask 最终只产生一个两头数据文件。
  4. Copy 阶段:ReduceTask 启动 Fetcher 线程到曾经实现 MapTask 的节点上复制一份属于本人的数据,这些数据默认会保留在内存的缓冲区中,当内存的缓冲区达到肯定的阀值的时候,就会将数据写到磁盘之上。
  5. Merge 阶段:在 ReduceTask 近程复制数据的同时,会在后盾开启两个线程对内存到本地的数据文件进行合并操作。
  6. Sort 阶段:在对数据进行合并的同时,会进行排序操作,因为 MapTask 阶段曾经对数据进行了部分的排序,ReduceTask 只需保障 Copy 的数据的最终整体有效性即可。
正文完
 0