spark core源码阅读-RDD详细介绍(四)

之前对RDD有一个大体概念了解,这一节我们需要深入了解它,了解在源码中到底是什么,
怎么产生的,给个rdd之间怎么依赖的,最后又怎么生成spark job

我们还是先从一个例子下手:

val sc = new SparkContext(sparkConf)

val res = sc
  .textFile(args(0))
  .flatMap(line => line.split(" "))
  .map(w => (w, 1))
  .reduceByKey(_ + _)
  .collect()

res.foreach(tuple => println(tuple._1 + " => " + tuple._2))

RDD如何生成

SparkContext是spark方法的主要的入口,代表着与spark集群建立连接了,它被用来创建RDDs,accumulators(累加器),
broadcast var(广播变量),每个JVM只容许一个SPARK-2243,
这里我们只讨论与RDDs相关方法:

方法 功能 生成RDD
textFile 从hadoop所支持的文件系统上读取文件 HadoopRDD
parallelize 把本地的数据集合分发行程RDD,懒加载 ParallelCollectionRDD
range 原理同上,只是做了一些分装 ParallelCollectionRDD => MapPartitionsRDD
makeRDD 同parallelize 生成ParallelCollectionRDD
wholeTextFiles 从hadoop所支持的文件系统上读取文件目录下所有文件 WholeTextFileRDD
binaryFiles 读二进制文件 BinaryFileRDD

通过对SparkContext的部分方法解释,我们能看出最开始的RDD怎么行程的,我先针对HadoopRDD看都有哪些内部成员:

  • `getDependencies: 获取依赖RDDs,我们发现是Nil很容易理解,第一个RDD,之前没有依赖

  • sc SparkContext: spark上下文

  • inputFormatClass<:InputFormat: hadoop中InputFormat负责生成InputSplit(文件块),实现RecordReader,读数据,
    我们也看出spark与hadoop的内在联系了

  • getPartitions: 通过inputFormat.getSplits得到文件块,然后包装成HadoopPartition,至此得到了该RDD的分区数了

  • compute: 先通过inputFormat.getRecordReader获取RecordReader,分装成自己的NextIterator,该迭代器最主要的
    方法hasNext,next,细节我们就略掉;最后与SparkContext一起分装成InterruptibleIterator

我们大概了解此RDD主要功能,借助hadoop inputFormat的获取文件块/对文件块读取,然后分装成自己的格式比如:
HadoopPartition,InterruptibleIterator

再看一个ParallelCollectionRDD,该RDD主要对本地数据集合分发到集群上执行:
- getPartitions: ParallelCollectionRDD.slice把数据集根据numSlices分片数量切分成一组一组数据,
然后用ParallelCollectionPartition分装
- compute: 把Seq集合与sparkContext分装到InterruptibleIterator

至此我们基本有一个大致的感官,这些初始RDD基本通过getPartitions分好区,再通过compute
partition包装成InterruptibleIterator,需要注意的是此时并没有真实的处理数据,只是做准备工作,但是next
可以直接获取该分区下的下一条数据.

接着上面的例子rdd.flatMap生成MapPartitionsRDD(pre,f)前一个RDD,compute中使用的函数(spark到处都是函数式编程):
- getPartitions: 前一个RDD的partition
- compute: 执行成员变量f,上面的例子中line => line.split(" ")空格分割返回数组,这是一个表达式,
iter.flapMap(f)是这个RDD主要功能,写一个直观的小例子:

val res = Seq(
    "Return a new RDD by applying",
    "a function to all elements of this RDD"
  ).flatMap(line=>line.split(" "))
  println(res)

//执行结果如下:
//List(Return, a, new, RDD, by, applying, a, function, to, all, elements, of, this, RDD)
  • iterator: 这是RDD内部公用的不可变方法,先从cache中读取,如果没有再去计算,不能被直接调用,用户可以在自定义RDD中调用

MapPartitionsRDD.map(w => (w, 1))生成MapPartitionsRDD,f是的主要逻辑是iter.map(cleanF),转换成键值对,

mapPartitionsRDD.reduceByKey(_ + _),有同学发现RDD中没有这个方法,而PairRDDFunctions却有,那么就引入一个问题:

PairRDDFunctions与RDD关系

PairRDDFunctions构造函数中接受的是RDD[(K, V)这种格式,我们可以推断出mapPartitionsRDD.reduceByKey(_ + _)
真实执行逻辑new PairRDDFunctions(mapPartitionsRDD).reduceByKey(_ + _),这其实涉及到了scala中隐式转换,
而我们确实在object RDD中发现了implicit def rddToPairRDDFunctions[K, V](rdd: RDD[(K, V)]),说白了就是你定义好
隐式转换的方法,编译器在编译过程中帮你自动根据类型与转换转换方法自动转换,这样代码写上去行云流水很顺畅:
我们写一个小例子:

class MyRDD {
   
  def map(): Unit = {
    println("this map")
  }

  override def toString = s"MyRDD()"
}

object MyRDD {
   
  implicit def rddToPairRDDFunctions(rdd: MyRDD): MyPairRDDFunctions = {
    new MyPairRDDFunctions(rdd)
  }
}

class MyShuffledRDD(prev: MyRDD) extends MyRDD {
   
  override def toString = s"MyShuffledRDD()"
}

class MyPairRDDFunctions(self: MyRDD) {
   
  def reduceByKey(): MyShuffledRDD = {
    println("this reduceByKey")
    new MyShuffledRDD(self)
  }
}

def main(args: Array[String]): Unit = {
  println(new MyRDD().reduceByKey())
}

执行结果为:

this reduceByKey
MyShuffledRDD()

如果理解了上面这块代码,那么也就清楚了RDD,PairRDDFunctions之间的内在联系,
下面继续往下跟代码reduceByKey,最终跟到此方法combineByKeyWithClassTag,发现最终返回ShuffledRDD,
根据上面对RDD分析我们基本可以确定只要分析以下方法:
- getPartitions: 根据Partitioner.numPartitions(这里其实就是文件块数量),然后返回Array[ShuffledRDDPartition]
- getDependencies: 返回ShuffleDependency(Dependency TODO 需要详谈)
- compute: 次方法是在Executor中的执行ShuffleMapTask的时候被执行,主要逻辑是combineCombinersByKey,然后落地到内存或者文件
这里说的很笼统,细节我们后面专门写一节详谈(TODO)

这个例子最后collect,RDD提交j

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值