Spark RDD内存数据集分配 用源码让你更快理解原理

1.先码一个demo
object RDD_Memory_Par {
  def main(args: Array[String]): Unit = {
//    TODO 准备环境
    val sparkConf = new SparkConf()
                            .setMaster("local[*]")
                                .setAppName("RDD_Memory_Par")
                                   .set("spark.default.parallelism","5")
    val sc = new SparkContext(sparkConf)

//    TODO 创建RDD
//    考虑RDD的并行度 & 分区
    val rdd = sc.makeRDD(List(1, 2, 3, 4,5),3)
//    将处理的数据保存成分区文件
    rdd.saveAsTextFile("scala01/data/output01")
//   TODO 关闭环境
    sc.stop()
  }
}
2.看结果

在这里插入图片描述

生成三个文件,文件数据分别为(1)、(2, 3)、(4, 5)

3.看源码

scala看源码一定要注意找到你要看的对象,再找到你最后一步要看的方法

我们现在要剖析的是为什么会造成RDD的数据集这样分配的,那我们先看RDD如何生成的。

​ val rdd = sc.makeRDD(List(1, 2, 3, 4,5),3)

无疑是这一句,那我就进入makeRDD的源码剖析。

点击进入后,会发现这一段源码。

def makeRDD[T: ClassTag](    
	seq: Seq[T],  
	numSlices: Int = defaultParallelism): RDD[T] = withScope { 
parallelize(seq, numSlices)
}

很明显,这里没有你要的计算方法,那我们就跟随对象,继续探索。

点击parallelize查看源码

def parallelize[T: ClassTag](    
	seq: Seq[T],    
	numSlices: Int = defaultParallelism): RDD[T] = withScope {  
		assertNotStopped()  
		new ParallelCollectionRDD[T](
		this, seq, numSlices, Map[Int, Seq[String]]()
	)
}

看不见分配方法,继续点击对象。

点击ParallelCollectionRDD查看源码

进入之后,下滑,看见了我们想看的分区方法

override def getPartitions: Array[Partition] = { 
val slices = ParallelCollectionRDD.slice(data, numSlices).toArray  slices.indices.map(i => new ParallelCollectionPartition(id, i, slices(i))).toArray}

到这一步会看见这里通过ParallelCollectionRDD定义了slices的常量,后面又进行了map的切分,好像感觉快到了,但是数据的分配如何计算的我们还是没有看见,那愣着干嘛,继续深入。

点击ParallelCollectionRDD查看源码。
点击进入之后发现是一个private Object,是一个执行对象,感觉计算方法应该在这个里面。

下滑没发现了一长串的模式匹配。

seq match {
      case r: Range =>
        positions(r.length, numSlices).zipWithIndex.map { case ((start, end), index) =>
          // If the range is inclusive, use inclusive range for the last slice
          if (r.isInclusive && index == numSlices - 1) {
            new Range.Inclusive(r.start + start * r.step, r.end, r.step)
          }
          else {
            new Range(r.start + start * r.step, r.start + end * r.step, r.step)
          }
        }.toSeq.asInstanceOf[Seq[Seq[T]]]
      case nr: NumericRange[_] =>
        // For ranges of Long, Double, BigInteger, etc
        val slices = new ArrayBuffer[Seq[T]](numSlices)
        var r = nr
        for ((start, end) <- positions(nr.length, numSlices)) {
          val sliceSize = end - start
          slices += r.take(sliceSize).asInstanceOf[Seq[T]]
          r = r.drop(sliceSize)
        }
        slices
      case _ =>
        val array = seq.toArray // To prevent O(n^2) operations for List etc
        positions(array.length, numSlices).map { case (start, end) =>
            array.slice(start, end).toSeq
        }.toSeq
    }

这部分就是对我们数据集的匹配,明显前面两个range匹配与我们不符,查看最后一个其他匹配,在这里面拿到了数据集的长度 val array = seq.toArray, array.length以及分区数numSlices,通过positions函数进行计算。

那我点击positions看看它是怎么被计算的

def positions(length: Long, numSlices: Int): Iterator[(Int, Int)] = {
      (0 until numSlices).iterator.map { i =>
        val start = ((i * length) / numSlices).toInt
        val end = (((i + 1) * length) / numSlices).toInt
        (start, end)
      }
    }

点击进入之后终于发现了我们想要分配计算方法

0 until numSlices:0-分区数,在我们的demo里就是0-3,until为开区间。

val start = ((i * length) / numSlices).toInt:i为第几个分区,length为数据集数据长度,numSlices为分区数,计算得出的start即这个i应该存放的第一个数据索引,以第一个分区为例:我们这里就是第一个分区存放的第一个索引为0,也就是第一个数据1。

val end = (((i + 1) * length) / numSlices).toInt:同理,这里就确定了一个分区里最后一个索引为何,同样以第一个分区为例:(0+1)*5/3 即得到的为1。

所以第一个分区存放的索引为 [0,1),转换为数据即1。

其他分区以此同理。

计算得出:

0分区:1

1分区:2,3

2分区:4,5

没有看懂的小伙伴,如果想了解,给我联系方式,手把手帮助哦!

帮忙点个关注吧^.^

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值