Spark从文件中创建RDD的分区机制源码解析

从磁盘读文件并创建RDD

sc.textFile(path)

一、从SparkContext.scala中找到该方法

//第一个参数为文件路径,第二个参数为最小分区数,默认值为defaultMinPartitions
def textFile(
    path: String,
    minPartitions: Int = defaultMinPartitions): RDD[String] = withScope {
  assertNotStopped()
  hadoopFile(path, classOf[TextInputFormat], classOf[LongWritable], classOf[Text],
    minPartitions).map(pair => pair._2.toString).setName(path)
}

//追踪第二个参数默认值minPartitions的默认值defaultMinPartitions
def defaultMinPartitions: Int = math.min(defaultParallelism, 2)
//追踪第一个参数defaultParallelism
// 一路追踪到LocalSchedulerBackend类中
override def defaultParallelism(): Int =
  scheduler.conf.getInt("spark.default.parallelism", totalCores)
  //即defaultParallelism为默认的服务器的总核数,我们的虚拟机中设置为4 
//所以,minPartitions的默认值defaultMinPartitions为2

textFile方法调用了hadoopFile方法,一路追踪:

二、进入hadoopFile方法(最后一个参数为minPartitions 2)

new HadoopRDD(
    this,
    confBroadcast,
    Some(setInputPathsFunc),
    inputFormatClass,
    keyClass,
    valueClass,
    minPartitions).setName(path)
}

三、进入HadoopRDD类中找到getPartitions方法(该方法调用了minPartitions参数)

override def getPartitions: Array[Partition] = {
  val jobConf = getJobConf()
  // add the credentials here as this can be called before SparkContext initialized
  SparkHadoopUtil.get.addCredentials(jobConf)
  val inputFormat = getInputFormat(jobConf)
  val inputSplits = inputFormat.getSplits(jobConf, minPartitions)
  val array = new Array[Partition](inputSplits.size)
  for (i <- 0 until inputSplits.size) {
    array(i) = new HadoopPartition(id, i, inputSplits(i))
  }
  array
}
<--注释:注意inputSplits方法,追踪如下-->
//其中
val inputSplits = inputFormat.getSplits(jobConf, minPartitions)//第二个参数为2

//进入getSplits方法
//追踪至FileInputFormat类的getSplits方法
//该方法第二个参数为传入的minPartitions,值为2
public InputSplit[] getSplits(JobConf job, int numSplits)
  throws IOException {
    //这里totalSize是文件总大小(字节数),假设第一次创建RDD为18个字节
    //第二次创建RDD假设totalSize为17
//所以第一次goalSize就为18/2=9
//第二次goalSize为17/2=8,注意这里的goalSize数据类型为long
    long goalSize = totalSize / (numSplits == 0 ? 1 : numSplits);
//minSplitSize为1,所以minSize也为1
  long minSize = Math.max(job.getLong(org.apache.hadoop.mapreduce.lib.input.
    FileInputFormat.SPLIT_MINSIZE, 1), minSplitSize);
//块大小为32M,Windows本地模式
      if (isSplitable(fs, path)) {
        long blockSize = file.getBlockSize();//32
        long splitSize = computeSplitSize(goalSize, minSize, blockSize);


// 第一次splitSize= Math.max(1, Math.min(9, 32))=9
//第二次splitSize= Math.max(1, Math.min(8, 32))=8
protected long computeSplitSize(long goalSize, long minSize,
                                     long blockSize) {
  return Math.max(minSize, Math.min(goalSize, blockSize));
}


        while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
//SPLIT_SLOP为1.1
//第一次 第一轮循环18/9=2>1.1 第二轮循环9/9=1<1.1所以当文件为18字节时分区为2
//第二次 第一轮循环为17/8=2.125>1.1 第二轮循环9/8=1.125>1.1,第三轮循环1/8<1,所以为3个分区

          String[] splitHosts = getSplitHosts(blkLocations,
              length-bytesRemaining, splitSize, clusterMap);
          splits.add(makeSplit(path, length-bytesRemaining, splitSize,
              splitHosts));
          bytesRemaining -= splitSize;
        }

        if (bytesRemaining != 0) {
          String[] splitHosts = getSplitHosts(blkLocations, length
              - bytesRemaining, bytesRemaining, clusterMap);
          splits.add(makeSplit(path, length - bytesRemaining, bytesRemaining,
              splitHosts));
        }
      } else {
        String[] splitHosts = getSplitHosts(blkLocations,0,length,clusterMap);
        splits.add(makeSplit(path, 0, length, splitHosts));
      }
    } else { 
      //Create empty hosts array for zero length files
      splits.add(makeSplit(path, 0, length, new String[0]));
    }
  }

总结:从文件创建RDD时,默认的分区数与虚拟机的内核数有关,还与文件的大小有关
本例中,第一次创建RDD文件大小为18字节,默认值有2个分区
第二次创建RDD时,文件大小为17字节,所以默认值有3个分区

猜你喜欢

转载自blog.csdn.net/qq_26502245/article/details/88369642