Hadoop MapReduce Splits 切片源码分析及切片原理

MapReducer编程之数据分片Split源码解读

数据切片核心源码
// Create the splits for the job
LOG.debug("Creating splits at " + jtFs.makeQualified(submitJobDir));
// TODO 数据是如何分片的 核心方法入口
int maps = writeSplits(job, submitJobDir);
conf.setInt(MRJobConfig.NUM_MAPS, maps);
LOG.info("number of splits:" + maps);
  private int writeSplits(org.apache.hadoop.mapreduce.JobContext job,
      Path jobSubmitDir) throws IOException,
      InterruptedException, ClassNotFoundException {
    JobConf jConf = (JobConf)job.getConfiguration();
    int maps;
    if (jConf.getUseNewMapper()) {
      // TODO 数据分片新API 入口  
      maps = writeNewSplits(job, jobSubmitDir);
    } else {
      maps = writeOldSplits(jConf, jobSubmitDir);
    }
    return maps;
  }
  private <T extends InputSplit>
  int writeNewSplits(JobContext job, Path jobSubmitDir) throws IOException,
      InterruptedException, ClassNotFoundException {
    Configuration conf = job.getConfiguration();
    // TODO 通过反射拿到InputFormat
    // TODO InputFormat是什么?已经明确知道现在使用的InputFormat,在MapReduce里面如果你想读数据一定要用到InputFormat,只不过是InputFormat的某个子类
    InputFormat<?, ?> input =
      ReflectionUtils.newInstance(job.getInputFormatClass(), conf);
	// TODO 拿到数据分片数
    // TODO 到这里,我们可以初步理解 假设文件是200M,默认SplitSize是128M,则这里得到的splits就是2
    // 200M --> 被分成 128M 和 72M
    // InputSplit 被一个Mapper处理  等同于Block
    List<InputSplit> splits = input.getSplits(job);
    T[] array = (T[]) splits.toArray(new InputSplit[splits.size()]);

    // sort the splits into order based on size, so that the biggest
    // go first
    Arrays.sort(array, new SplitComparator());
    JobSplitWriter.createSplitFiles(jobSubmitDir, conf, 
        jobSubmitDir.getFileSystem(conf), array);
    return array.length;
  }
  /** 
   * 得到一个files集合,把它放到FileSplits(InputSplit的一个实现)
   * 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 {
    StopWatch sw = new StopWatch().start();
    // 从这里开始计算分片  
    // getFormatMinSplitSize() 源码中写死了,值为 1
	// getMinSplitSize(job) 从参数:mapreduce.input.fileinputformat.split.minsize 中得到
    // 如果没有拿到,则取默认值:1L
    // 获取最大值 max(1,1L) 最终minSize=1
    long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
    // getMaxSplitSize(job) 的值从参数 mapreduce.input.fileinputformat.split.maxsize 获取,如     // 果没有设置,为默认值:Long.MAX_VALUE 即为Long类型的最大值:2的63次方减1。
    // 最终 maxSize = 2的63次方减1
    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)) {
          // 获取BlockSize的大小,默认128M(注意:在服务器上,Windows本地是32M)
          long blockSize = file.getBlockSize();
          // splitSize:每个文件被切成多大
          // computeSplitSize 方法中是获取最大值:
          // Math.max(minSize, Math.min(maxSize,blockSize)); 
          // 这里minSize是1;maxSize是2的63次方减1;blockSize是128M
          // 则  splitSize = 128M
          long splitSize = computeSplitSize(blockSize, minSize, maxSize);
	      
          // bytesRemaining : 剩余的字节(就是文件还剩多少字节,初始值length是文件的大小)
          long bytesRemaining = length;
          
          // ***** (五颗星) SPLIT_SLOP: 源码中写死了,是1.1
          // 接下来开始切分文件 while条件(文件大小/128M > 1.1)
          // 这里当while条件为true时才会对文件进行切分。 
          // 假如文件大小是 129M ==> 则:129M/128M < 1.1 
          // 此时该文件会被当作一个Split去处理  
          while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
            int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
            splits.add(makeSplit(path, length-bytesRemaining, splitSize,
                        blkLocations[blkIndex].getHosts(),
                        blkLocations[blkIndex].getCachedHosts()));
            bytesRemaining -= splitSize;
          }

          if (bytesRemaining != 0) {
            int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
            splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
                       blkLocations[blkIndex].getHosts(),
                       blkLocations[blkIndex].getCachedHosts()));
          }
        } else { // not splitable
          splits.add(makeSplit(path, 0, length, blkLocations[0].getHosts(),
                      blkLocations[0].getCachedHosts()));
        }
      } 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());
    sw.stop();
    if (LOG.isDebugEnabled()) {
      LOG.debug("Total # of splits generated by getSplits: " + splits.size()
          + ", TimeTaken: " + sw.now(TimeUnit.MILLISECONDS));
    }
    // 将分片数返回  
    return splits;
  }
Debug流程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

1、当文件 < BlockSize(默认128M)时:文件个数 = 分片的个数 = maptask个数

2、当文件 > BlockSize(默认128M)时 并且 (文件大小/BlockSize > 1.1) 时,文件会被切分

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值