map任务和reduce任务个数如何计算

来源:互联网 发布:机地手机维修淘宝 编辑:程序博客网 时间:2024/05/21 17:17

一 MapTask个数的决定因素

首先,我们需要明确以下几点:

1Map Task个数不能通过配置文件指定

2Map Task个数是在进行文件的切分时动态计算的

3FileInputFormat负责切分文件进行split操作

1.1分析源码:

intmaps = writeSplits(job, submitJobDir);

privateint writeSplits(org.apache.hadoop.mapreduce.JobContextjob,

      PathjobSubmitDir) throws IOException,

      InterruptedException,ClassNotFoundException {

    JobConfjConf = (JobConf)job.getConfiguration();

    int maps;

    //判断是否采用新的API,现在我们应该都是新的

    if (jConf.getUseNewMapper()) {

      maps = writeNewSplits(job,jobSubmitDir);

    } else {

      maps = writeOldSplits(jConf,jobSubmitDir);

    }

returnmaps;

}

private <Textends InputSplit> int writeNewSplits(JobContext job, Path jobSubmitDir) throws IOException,

      InterruptedException,ClassNotFoundException {

    Configurationconf = job.getConfiguration();

    //创建FileInputFormat

    InputFormat<?, ?> input =

      ReflectionUtils.newInstance(job.getInputFormatClass(),conf);

    //调用FileInputFormat#getSplits

    List<InputSplit> splits =input.getSplits(job);

    T[]array = (T[]) splits.toArray(newInputSplit[splits.size()]);

 

    //对split数组元素进行排序,最大的是第一个

    Arrays.sort(array,new SplitComparator());

    //创建Split文件,这些个文件会存在提交路径的临时目录

    JobSplitWriter.createSplitFiles(jobSubmitDir,conf,

       jobSubmitDir.getFileSystem(conf),array);

    return array.length;

  }

 

publicList<InputSplit>getSplits(JobContextjob) throws IOException {

    StopWatchsw = newStopWatch().start();

    //根据mapreduce.input.fileinputformat.split.minsize配置和1取最大的

    long minSize = Math.max(getFormatMinSplitSize(),getMinSplitSize(job));

    //根据mapreduce.input.fileinputformat.split.maxsize取最大的

    long maxSize =getMaxSplitSize(job);

 

    // generate splits

    List<InputSplit> splits =new ArrayList<InputSplit>();

    List<FileStatus> files = listStatus(job);

    for (FileStatus file:files) {

      //获取文件路径

      Pathpath = file.getPath();

      //获取文件大小

      long length =file.getLen();

      if (length !=0) {

        BlockLocation[]blkLocations;

       //从本地获取文件数据块位置

       if (fileinstanceof LocatedFileStatus) {

         blkLocations = ((LocatedFileStatus) file).getBlockLocations();

        }else {//非本地文件,远程调用获取文件数据块信息

         FileSystem fs =path.getFileSystem(job.getConfiguration());

         blkLocations = fs.getFileBlockLocations(file,0, length);

        }

       if (isSplitable(job,path)) {

         //获取文件数据块大小,默认128M

         long blockSize =file.getBlockSize();

         //计算InputSplit大小

         long splitSize =computeSplitSize(blockSize,minSize, maxSize);

 

         //将bytesRemaining(剩余未分片字节数)设置为整个文件的长度

         long bytesRemaining =length;

         /*

           *若剩余值bytesRemaining > 1.1 * splitSize,则继续对文件进行逻辑切分

           *若小于这个值,则作为一个InputSplit

           */

         while (((double)bytesRemaining)/splitSize >SPLIT_SLOP) {

           //计算文件的数据块的索引,只是计算InputSplit的起始位置是否位于某一块中

           int blkIndex =getBlockIndex(blkLocations,length-bytesRemaining);

           //然后将计算的索引位置作为参数计算切分的split文件,然后添加到split数组

           splits.add(makeSplit(path,length-bytesRemaining,splitSize,

                       blkLocations[blkIndex].getHosts(),

                       blkLocations[blkIndex].getCachedHosts()));

           /*

             *剩余字节数-splitSize,相当于下一次从这儿开始计算

             *我们也可以推断出起始位置为0,splitSize,2*splitSize,3*splitSize 等

             */

           bytesRemaining -= splitSize;

          }

         //如果block中剩下的一小段数据量小于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 lengthfiles

       splits.add(makeSplit(path,0, length, new String[0]));

      }

    }

    // Save the number of input files for metrics/loadgen

    //设置mapreduce.input.fileinputformat.numinputfile值为输入文件数量

    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;

}

 

publicstatic <T extendsInputSplit> voidcreateSplitFiles(Path jobSubmitDir,

      Configurationconf, FileSystemfs, T[] splits)

  throws IOException, InterruptedException {

    /*

     *创建切片文件,并获取FSDataOutputStream对应路径jobSubmitDir

     *届时就会生成${jobSubmitDir}/job.split文件

     * jobSubmitDir:参数yarn.app.mapreduce.am.staging-dir

     *指定的路径

     */

    FSDataOutputStreamout = createFile(fs,

        JobSubmissionFiles.getJobSplitFile(jobSubmitDir),conf);

    //将切片数据写入切片文件,并得到切片元数据信息数组

    SplitMetaInfo[]info = writeNewSplits(conf,splits, out);

    out.close();

    //将切片元数据信息写入切片元数据信息文件

    writeJobSplitMetaInfo(fs,JobSubmissionFiles.getJobSplitMetaFile(jobSubmitDir),

       new FsPermission(JobSubmissionFiles.JOB_FILE_PERMISSION),splitVersion,

       info);

  }

  private static FSDataOutputStreamcreateFile(FileSystemfs, Path splitFile,

      Configurationjobthrows IOException {

    FSDataOutputStreamout = FileSystem.create(fs, splitFile,

       new FsPermission(JobSubmissionFiles.JOB_FILE_PERMISSION));

    //获取副本数,默认是10

    int replication =job.getInt(Job.SUBMIT_REPLICATION,10);

    fs.setReplication(splitFile, (short)replication);

    //写入切片头信息

    writeSplitHeader(out);

    return out;

  }

 

#遍历输入的文件

#获取文件数据块的位置以及文件数据块的大小(默认128m)

#计算分片的尺寸大小splitSize

#对文件数据块进行分片

#创建切片文件,写入头信息,文件位置位于提交job的路径

#将分片信息写入分片文件,并将得到的切片元数据信息写入切片元数据信息文件

1.2Map任务的决定因素

我们知道,map的个数是intmaps = writeSplits(job, submitJobDir);

这里产生的,也就是取决于切片数量。

那么切片数量又是由什么决定的呢?

>如果splitSize== blockSize(128M),那么只有一个切片

也就是一个Map 任务

>如果minSize超过blockSize,那么根据计算splitSize算法,会取128M和minSize中最大的,所以会减少分片数量,也就是会减少MapTask数量

>如果maxSize< blockSize,那么会选择之间比较小的然后跟minSize比较取较大者,那么这样这会增加分片数量,从而增加Map Task

总结:决定因素

# mapreduce.input.fileinputformat.split.minsize

# mapreduce.input.fileinputformat.split.maxsize

# blockSize

 

二 ReduceTask的决定因素

 

reduce在运行时往往需要从相关map端复制数据到reduce节点来处理,因此相比于map任务。reduce节点资源是相对比较缺少的,同时相对运行较慢,正确的reduce任务的个数应该是0.95或者1.75 *(节点数* mapred.tasktracker.tasks.maximum参数值)。如果任务数是节点个数的0.95倍,那么所有的reduce任务能够在 map任务的输出传输结束后同时开始运行。如果任务数是节点个数的1.75倍,那么高速的节点会在完成他们第一批reduce任务计算之后开始计算第二批 reduce任务,这样的情况更有利于负载均衡。同时需要注意增加reduce的数量虽然会增加系统的资源开销,但是可以改善负载匀衡,降低任务失败带来的负面影响。同样,Reduce任务也能够与 map任务一样,通过设定JobConf的conf.setNumReduceTasks(intnum)方法来增加任务个数。

 

阅读全文
0 0
原创粉丝点击