MapReduce框架原理之InputFormat的数据输入

InputFormat数据输入

MapReduce流程
MapReduce流程

切片与MapReduce并行度决定机制

  1. 问题引出

    MapTask的并行度决定Map阶段的任务处理并发度,进而影响到整个Job的处理速度。

    思考:1G的数据,启动8个MapTask,可以提高集群的并发处理能力。那么1K的数据,启动8个MapTask,会提高集群性能吗?MapTask并行任务是否越多越好呢?那些因素影响了MapTask并行度?

  2. MapTask并行度决定机制

    数据块:Block是HDFS物理把数据分成一块一块。数据块是HDFS存储数据单位。

    数据切片:数据切片只是逻辑上对输入数据进行分片,并不会在磁盘上将其切分成片进行存储。数据切片是MapReduce程序计算输入数据的单位,一个切片会对应启动一个MapTask

数据切片与MapTask并行度决定
数据切片与MapTask并行度决定

如图所示:

  • 逻辑切片为100M的时候,虽然启动了三个MapTask但是,会存在不完整数据,需要在不同的节点之间进行传输数据,效率会比较低
  • 当逻辑切片为128M等于默认值BlockSize的时候,此时三个MapTask全部在本地执行,不会存在数据传输的问题

Job提交流程源码和切片源码详解

  1. Job提交流程源码详解

    waitForCompletion()

    submit();

    // 1建立连接
     connect(); 
      // 1)创建提交Job的代理
      new Cluster(getConfiguration());
       // (1)判断是本地运行环境还是yarn集群运行环境
       initialize(jobTrackAddr, conf); 

    // 2 提交job
    submitter.submitJobInternal(Job.this, cluster)

     // 1)创建给集群提交数据的Stag路径
     Path jobStagingArea = JobSubmissionFiles.getStagingDir(cluster, conf);

     // 2)获取jobid ,并创建Job路径
     JobID jobId = submitClient.getNewJobID();

     // 3)拷贝jar包到集群
    copyAndConfigureFiles(job, submitJobDir); 
     rUploader.uploadFiles(job, jobSubmitDir);

     // 4)计算切片,生成切片规划文件
    writeSplits(job, submitJobDir);
      maps = writeNewSplits(job, jobSubmitDir);
      input.getSplits(job);

     // 5)向Stag路径写XML配置文件
    writeConf(conf, submitJobFile);
     conf.writeXml(out);

     // 6)提交Job,返回提交状态
    status = submitClient.submitJob(jobId, submitJobDir.toString(), job.getCredentials());

    Job提交流程源码解析
    Job提交流程源码解析
  2. FileInputFormat 切片源码解析(input.getSplits(job))

    • 程序先找到你数据存储的目录

    • 开始遍历处理(规划切片)目录下的每一个文件

    • 遍历第一个文件ss.txt

      • 获取文件大小fs.sizeOf(ss.txt)

      • 计算切片大小

        computeSplitSize(Math.max(minSize,Max.min(maxSize,blocksize)))=blocksize=128M

      • 默认情况下,切片大小=blocksize

      • 开始切,形成第一个切片ss.txt-0:128M 第二个切片ss.txt-128:256M 第三个切片ss.txt-256M:300M

        每次切片时,都要判断切完剩下的部分是否大于块的1.1倍,不大于1.1倍就划分一块切片

      • 将切片信息写到一个切片规划文件中

      • 整个切片的核心过程在getSplit()方法中完成

      • InputSplit只记录了切片的元数据信息,比如起始位置,长度以及所在的节点列表等

    • 提交切片规划文件到YARN上,YARN上的MrAppMaster就可以根据切片规划文件开始计算MapTask的个数

FileInputFormat 切片机制

  1. 切片机制

    • 简单地按照文件的内容长度进行切片
    • 切片大小,默认等于Block大小
    • 切片时不考虑数据集整体,而是逐个针对每一个文件单独切片
  2. 案例分析

    • 输入数据有两个文件:

      file1.txt 320M

      file2.txt 10M

    • 经过FileInputFormat的切片机制进行运算后,形成的切片信息如下:

      file1.txt.split1-- 0~128

      file1.txt.split2-- 128~256

      file1.txt.split-- 256~320

      file2.txt.split1-- 0~10M

    • 源码中计算切片大小的公式

      Math.max(minSize,Math.min(maxSize,blockSize));

      mapreduce.input.fileinputformat.split.minsize=1 默认值为1

      mapreduce.input.fileinputformat.splitmaxsize=Long.MAXValue 默认值为Long.MAXValue

      因此,默认情况下,切片大小=blocksize

    • 切片大小设置

      maxsize(切片最大值):参数如果调的比blocksize小,则会让切片变小,而且就等于配置这个参数的值

      minsize(切片最大值):参数如果把blocksize大,则可以让切片变得比blocksize还大

    • 获取信息API

      //获取切片的文件名称
      String name = inputSplit.getPath().getName();
      //根据文件类型获取切片信息
      FileSplit inputSplit = (FileSplit) context.getInputSplit()

      Context 是MapReduce任务运行的一个上下文,包含了整个任务的全部信息

TextInputFormat

  1. FileInputFormat实现类

    思考:在运行MapReduce程序时,输入的文件格式包括:基于行的日志文件、二进制文件、数据库表等。那么,针对不同的数据类型,MapReduce是如何读取这些数据的呢?

    FileInputFormat常见的接口实现类包括:TextInputFormat、KeyValueTextInputFormat 、NLineInputFormat、CombineTextInputFormat和自定义InputFormat等。

  2. TextInputFormat

    TextInputFormat是默认的FileInputFormat实现类。每行读取每条记录。键是存储在该行在整个文件中的起始字节偏移量,LongWritable类型。值是这行的内容,不包括任何行终止执行符(换行符和回车符),Text类型。

    以下是一个示例,比如,一个切片包含了如何四条记录

    Rich learning form

    Intelligent learning engine

    Learning more convenient

    From the real demand for more close to the enterprise

    每条记录表示为以下键/值对

    (0,Rich learning form)

    (20,Intelligent learning engine)

    (49,Learning more convenient)

    (74,From the real demand for more close to the enterprise)

  3. CombineTextInputFormat 切片机制

    框架默认的TextInputFormat切片机制是对任务按文件规划切片,不管文件多小,都会开始一个单独的切片,都会交给一个MapTask,如果这样有大量小文件,就会产生大量的MapTask,处理效率及其低下。

  • 应用场景

    CombineTextInputFormat用于小文件过多的场景,他可以将多个小文件从逻辑规划到一个切片中,这样,多个小文件就可以交给一个MapTask处理。

  • 虚拟切片最大值设置

    CombineTextInputFormat.setMaxInputSplit(job,4194304); //4m

    注意:虚拟存储切片最大值设置最好根据实际的小文件大小情况来设置具体的值

  • 切片机制

    生成切片过程包括:虚拟存储过程和切片过程两部分

    combineTextInput切片机制
    combineTextInput切片机制
    • 虚拟存储过程

      将输入目录下所有文件大小,依次和设置的setMaxInputSplitSize值比较,如果不大于设置的最大值,逻辑上划分一个块。如果输入文件大于设置的最大值且大于两倍,那么以最大值切割一块;当剩余数据大小超过设置的最大值且不大于最大值2倍,此时将文件均分成2个虚拟存储块(防止出现太小切片)。

      例如setMaxInputSplitSize值为4M,输入文件大小为8.02M,则先逻辑上分成一个4M。剩余的大小为4.02M,如果按照4M逻辑划分,就会出现0.02M的小的虚拟存储文件,所以将剩余的4.02M文件切分成(2.01M和2.01M)两个文件。

    • 切片过程:

      (a)判断虚拟存储的文件大小是否大于setMaxInputSplitSize值,大于等于则单独形成一个切片。

      (b)如果不大于则跟下一个虚拟存储文件进行合并,共同形成一个切片。

      (c)测试举例:有4个小文件大小分别为1.7M、5.1M、3.4M以及6.8M这四个小文件,则虚拟存储之后形成6个文件块,大小分别为:

      1.7M,(2.55M、2.55M),3.4M以及(3.4M、3.4M)

      最终会形成3个切片,大小分别为:

      (1.7+2.55)M,(2.55+3.4)M,(3.4+3.4)M

      注意文件的排序此时是和切片息息相关的

CombineTextInputFormat案例实操

  • 需求

    将输入的大量小文件合并成一个切片统一处理。

    • 输入数据

    • 期望

      期望一个切片处理4个文件

    • 实现过程

      • 不做任何处理,继续运行wordCount案例程序,观察切片个数

        number of splits:4

      • 在WordcountDriver中增加如下代码,运行程序,并观察运行的切片个数为3。

        • 驱动类中添加代码如下:

          // 如果不设置InputFormat,它默认用的是TextInputFormat.class

          job.setInputFormatClass(CombineTextInputFormat.class);

          //虚拟存储切片最大值设置4m

          CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);

        • 运行结果为3个切片

          number of splits:3

      • 在WordcountDriver中增加如下代码,运行程序,并观察运行的切片个数为1。

        • 驱动中添加代码如下:

          // 如果不设置InputFormat,它默认用的是TextInputFormat.class

          job.setInputFormatClass(CombineTextInputFormat.class);

          //虚拟存储切片最大值设置20m

          CombineTextInputFormat.setMaxInputSplitSize(job, 20971520);

        • 运行结果为1个切片

          number of splits:1

MapReduce 工作流程

MapReduce工作流程图-1
MapReduce工作流程图-1
MapReduce工作流程图-2
MapReduce工作流程图-2

上面的流程就是整个MapReduce最全工作流程,但是Shuffle过程只是从第7步到第16步结束,具体Shuffle过程详解,如下:

  • MapTask收集我们的map()方法输出的kv对,放到内存缓冲区中
  • 从内存缓冲区中不断溢出本地磁盘文件,可能会溢出多个文件
  • 多个溢出文件会被合成最大的溢出文件
  • 在溢出过程及合并的过程中,都要调用Partitioner进行分区和针对key进行排序
  • ReduceTask根据自己的分区号,去各个MapTask机器上取相应的结果分区数据
  • ReduceTask会抓取到同一个分区的来自不同MapTask的结果文件,ReduceTask会将这些文件再进行合并(归并排序)
  • 合并成大文件后,Shuffle的过程也就结束了,后面进入ReduceTask的逻辑运算过程(从文件中取出一个一个的键值对Group,调用用户自定义的reduce()方法)

注意:

  • Shuffle中的缓冲区大小会影响到MapReduce程序的执行效率,原则上说,缓冲区越大,磁盘io的次数越少,执行速度就越快。
  • 缓冲区的大小可以通过参数调整,参数:mapreduce.task.io.sort.mb默认100M。
  • RecordReader:其作用就是将数据切分成key/value的形式然后作为输入传给Mapper。
  • 好文一篇:MapReduce中Combiner的作用
  • <key,valuelist>并非是在shuff过程最终的结果,而是Reduce任务在Map结束后拿到的结果
  • 归并排序适合已经排序好的数据
posted @ 2021-10-26 00:18  逆十字  阅读(77)  评论(0)    收藏  举报