从磁盘读文件并创建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个分区