MapReduce Application中mapper的数目和分片的数目

本文深入探讨了MapReduce框架中文件分片的生成过程,包括分片数量如何受文件大小及用户设定的map数量的影响。并通过源码分析,详细解释了Hadoop中FileInputFormat的getSplits方法的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题 MapReduce Application中mapper的数目和分片的数目是一样的

  • 默认情况下,分片和输入文件的分块数是相等的。也不完全相等,如果block size大小事128M,文件大小为128.1M,文件的block数目为2,但是application运行过程中,你会发现分片数目是1,而不是2,其中的机理,后面会分析

  • 有的程序会设置map的数目,那么map数目是怎样影响分片的数目的呢?

  • 如果文件大小为0,是否会作为一个分片传给map任务?



流程
FileInputFormat.getSplits返回文件的分片数目,这部分将介绍其运行流程,后面将粘贴其源码并给出注释

  • 通过listStatus()获取输入文件列表files,其中会遍历输入目录的子目录,并过滤掉部分文件,如文件_SUCCESS

  • 获取所有的文件大小totalSIze

  • goalSIze=totalSize/numMaps。numMaps是用户指定的map数目

  • files中取出一个文件file

  • 计算splitSize。splitSize=max(minSplitSize,min(file.blockSize,goalSize)),其中minSplitSize是允许的最小分片大小,默认为1B

  • 后面根据splitSize大小将file分片。在分片的时候,如果剩余的大小不大于splitSize*1.1,且大于0B的时候,会将该区域整个作为一个分片。这样做是为了防止一个mapper处理的数据太小

  • 将file的分片加入到splits中

  • 返回4,直到将files遍历完

  • 结束,返回splits

092534_o6ni_1775885.jpg 

源码(hadoop2.2.0)

其实流程算起来也不算复杂,所以就直接用代码注释来做吧

 

这里边涉及这么几个方法:

1、public List<InputSplit> getSplits(JobContext job), 这个由客户端调用来获得当前Job的所有分片(split),然后发送给JobTracker(新API中应该是ResourceManager),而JobTracker根据这些分片的存储位置来给TaskTracker分配map任务去处理这些分片。这个方法用到了后边的listStatus,然后根据得到的这些文件信息,从FileSystem那里去拉取这些组成这些文件的块的信息(BlockLocation),使用的是getFileBlockLocation(file,start,len),这个方法是与使用的文件系统实现相关的(FileSystem,LocalFileSystem,DistributedFileSystem)

/** 
   * Generate the list of files and make them into FileSplits.
   * @param job the job context
   * @throws IOException
   */
  public List<InputSplit> getSplits(JobContext job) throws IOException {
    long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
    long maxSize = getMaxSplitSize(job);

    // generate splits
    List<InputSplit> splits = new ArrayList<InputSplit>();
    List<FileStatus> files = listStatus(job);
    for (FileStatus file: files) {
      Path path = file.getPath();
      long length = file.getLen();
      if (length != 0) {
        BlockLocation[] blkLocations;
        if (file instanceof LocatedFileStatus) {
          blkLocations = ((LocatedFileStatus) file).getBlockLocations();
        } else {
          FileSystem fs = path.getFileSystem(job.getConfiguration());
          blkLocations = fs.getFileBlockLocations(file, 0, length);
        }
        if (isSplitable(job, path)) {
          long blockSize = file.getBlockSize();
          long splitSize = computeSplitSize(blockSize, minSize, maxSize);

          long bytesRemaining = length;
          while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
            int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
            splits.add(makeSplit(path, length-bytesRemaining, splitSize,
                                     blkLocations[blkIndex].getHosts()));
            bytesRemaining -= splitSize;
          }

          if (bytesRemaining != 0) {
            int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
            splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
                       blkLocations[blkIndex].getHosts()));
          }
        } else { // not splitable
          splits.add(makeSplit(path, 0, length, blkLocations[0].getHosts()));
        }
      } else { 
        //Create empty hosts array for zero length files
        splits.add(makeSplit(path, 0, length, new String[0]));
      }
    }
    // Save the number of input files for metrics/loadgen
    job.getConfiguration().setLong(NUM_INPUT_FILES, files.size());
    LOG.debug("Total # of splits: " + splits.size());
    return splits;
  }

2、protected List<FileStatus> listStatus(JobContext job), 先根据“mapred.input.dir”的配置值去得到用户指定的所有Path。然后根据这个JobContext的Configuration得到FileSystem(当然,更可能是 DistributedFileSystem )。最后应用用户可能设置了的PathFilter,通过FileSystem获取所有这些Path所代表的File(FileStatus)。注:这个方法的东西相当多,很多内容还十分陌生。

/** List input directories.
   * Subclasses may override to, e.g., select only files matching a regular
   * expression. 
   * 
   * @param job the job to list input paths for
   * @return array of FileStatus objects
   * @throws IOException if zero items.
   */
  protected List<FileStatus> listStatus(JobContext job
                                        ) throws IOException {
    List<FileStatus> result = new ArrayList<FileStatus>();
    Path[] dirs = getInputPaths(job);
    if (dirs.length == 0) {
      throw new IOException("No input paths specified in job");
    }
    
    // get tokens for all the required FileSystems..
    TokenCache.obtainTokensForNamenodes(job.getCredentials(), dirs, 
                                        job.getConfiguration());

    // Whether we need to recursive look into the directory structure
    boolean recursive = getInputDirRecursive(job);
    
    List<IOException> errors = new ArrayList<IOException>();
    
    // creates a MultiPathFilter with the hiddenFileFilter and the
    // user provided one (if any).
    List<PathFilter> filters = new ArrayList<PathFilter>();
    filters.add(hiddenFileFilter);
    PathFilter jobFilter = getInputPathFilter(job);
    if (jobFilter != null) {
      filters.add(jobFilter);
    }
    PathFilter inputFilter = new MultiPathFilter(filters);
    
    for (int i=0; i < dirs.length; ++i) {
      Path p = dirs[i];
      FileSystem fs = p.getFileSystem(job.getConfiguration()); 
      FileStatus[] matches = fs.globStatus(p, inputFilter);
      if (matches == null) {
        errors.add(new IOException("Input path does not exist: " + p));
      } else if (matches.length == 0) {
        errors.add(new IOException("Input Pattern " + p + " matches 0 files"));
      } else {
        for (FileStatus globStat: matches) {
          if (globStat.isDirectory()) {
            RemoteIterator<LocatedFileStatus> iter =
                fs.listLocatedStatus(globStat.getPath());
            while (iter.hasNext()) {
              LocatedFileStatus stat = iter.next();
              if (inputFilter.accept(stat.getPath())) {
                if (recursive && stat.isDirectory()) {
                  addInputPathRecursively(result, fs, stat.getPath(),
                      inputFilter);
                } else {
                  result.add(stat);
                }
              }
            }
          } else {
            result.add(globStat);
          }
        }
      }
    }

    if (!errors.isEmpty()) {
      throw new InvalidInputException(errors);
    }
    LOG.info("Total input paths to process : " + result.size()); 
    return result;
  }

3、protected long computeSplitSize(long blockSize, long minSize, long maxSize),计算出当前Job所配置的分片最大尺寸。

  protected long computeSplitSize(long blockSize, long minSize,
                                  long maxSize) {
    return Math.max(minSize, Math.min(maxSize, blockSize));
  }

4、protected int getBlockIndex(BlockLocation[] blkLocations, long offset), 由于组成文件的块的信息已经获得了,只需要根据offset来计算所在的那个块就行了。

  protected int getBlockIndex(BlockLocation[] blkLocations, 
                              long offset) {
    for (int i = 0 ; i < blkLocations.length; i++) {
      // is the offset inside this block?
      if ((blkLocations[i].getOffset() <= offset) &&
          (offset < blkLocations[i].getOffset() + blkLocations[i].getLength())){
        return i;
      }
    }
    BlockLocation last = blkLocations[blkLocations.length -1];
    long fileLength = last.getOffset() + last.getLength() -1;
    throw new IllegalArgumentException("Offset " + offset + 
                                       " is outside of file (0.." +
                                       fileLength + ")");
  }


 转自:MapReduce获取分片数Hadoop中FileInputFormat计算InputSplit的getSplits方法的流程

转载于:https://my.oschina.net/KingPan/blog/288549

### 回答1: MapReduce是一种分布式计算模型,其中的mapperreduce是两个重要的组件。 mapper负责将输入数据分割成小块,并将每个小块映射为一组键值对。这些键值对将被传递给reduce组件进行处理。 reduce组件负责将mapper产生的键值对进行合并归约,最终生成输出结果。 在MapReduce中,mapperreduce是并行执行的,可以在多个计算节点上同时运行,以提高计算效率。 ### 回答2: MapReduce是谷歌公司开发的一个分布式计算框架,它可以支持大规模的数据处理。MapReduce的核心思想是将数据分成若干个小块,然后在分布式计算集群上分别对这些数据块进行处理,最后将处理结果汇总。MapReduce中的mapperreduce是两个核心的组件,下面我们来详细介绍这两个组件。 Mapper MapperMapReduce中一个非常重要的组件,它主要负责对输入的数据进行拆分,转换过滤。Mapper的输入格式可以是任意的,常见的输入格式有文本、XML、CSV等。对于每一个输入数据,Mapper会按照一定的规则将其转换为一组<key, value>对,其中key是转换后数据的关键字,value是转换后数据的实际值。Mapper的输出会被送到一组reduce节点上进行处理。 Reduce Reduce是MapReduce中另一个重要的组件,它主要负责对 Mapper 的输出进行合并计算。Reduce的输入格式是Mapper输出的一组<key, value>对,同样也是一个集合。Reduce会对这个集合中的所有<key, value>对进行聚合操作,最终输出一个或多个最终结果。 在MapReduce的运行过程中,数据首先会被拆分成不同的块,然后这些块会被分配到集群中多个节点上进行批量处理。Mapper组件会对每一个块进行处理,Mapper可以在处理期间改变keyvalue,然后将处理结果输出到Reduce组件中。Reduce会在所有Mapper组件输出结果的基础上,聚合结果集并执行最后的计算。最后,MapReduce会将计算结果输出到文件系统中或其他存储介质中。 总的来说,MapReducemapperreduce组件可以将数据的繁琐处理任务分解到多个节点上同时执行,极大地提高了数据处理的效率速度。这种方法可以在大规模数据处理分析任务中发挥非常重要的作用。 ### 回答3: MapReduce是一个并行处理数据的框架。它是由Google公司开发并在2004年的一篇论文中首次提出,用于解决大规模数据处理的问题。MapReduce的工作流程主要包括两个阶段:Map阶段Reduce阶段。 在MapReduce中,MapperReducer是两个核心组件,它们在不同的阶段中扮演着重要的角色。Mapper的主要工作是将输入数据切分成若干个片段,并对每个数据片段执行一组操作,然后将结果输出到Reduce,这样Reduce就可以对Mapper输出的结果进行聚合操作。而Reducer的主要工作则是对Mapper输出的结果进行合并操作,最终输出一个或多个结果。 通常情况下,Mapper的输入是以键值对的形式提供的,而Mapper的输出也是以键值对的形式呈现的。这意味着,在Mapper中可以执行各种数据操作,如过滤、排序、合并、相加等。Mapper的一个重要特征是它经常能够并行化地执行。同样,Reducer也能处理输入键值对并并行化地执行,但输出通常是一个数据值的列表而不是一个键值对。 在MapReduce中,Reducer的数量可以是任意的,但通常都是由用户定义的。如果Reducer的数量比较少,则可能会导致性能问题。相反,如果Reducer的数量过多,则可能会影响MapReduce的总体性能。 总的来说,MapperReducer是MapReduce中最重要的组件之一。它们通过执行不同的操作,帮助大数据处理系统适应不同的需求复杂性水平。可以说,MapperReducer为大规模数据处理开辟了新的方向,这对于使用MapReduce大规模处理数据的企业组织来说是一个重要的趋势。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值