Spark源码解析(一)

RDD之getNarrowAncestors内部方法分析

最近开始spark的源码攻关,其实看源码一直是我最怕的东西,因为太多、太杂、太深导致不能够很好的把我脉络导致每次最后都放弃。有人跟我说看源码可以阶段性一个方法一个方法的去学习,去看,每天积累一点总会成功,那么今天开始我的第一天spark源码分析。

我这里从spark最基本的RDD中的方法说起,我感觉这样会更容易一些。同时我只对其中感觉比较重要的方法进行一些讲解。今天我主要讲一下getNarrowAncestors方法,也就是所谓的获取窄依赖的父RDD的方法,这个方法是一个内部方法,方法整体如下:


/**
 * Return the ancestors of the given RDD that are related to it only through a sequence of
 * narrow dependencies. This traverses the given RDD's dependency tree using DFS, but maintains
 * no ordering on the RDDs returned.
 */
private[spark] def getNarrowAncestors: Seq[RDD[_]] = {
  val ancestors = new mutable.HashSet[RDD[_]]

  def visit(rdd: RDD[_]) {
    val narrowDependencies = rdd.dependencies.filter(_.isInstanceOf[NarrowDependency[_]])
    val narrowParents = narrowDependencies.map(_.rdd)
    val narrowParentsNotVisited = narrowParents.filterNot(ancestors.contains)
    narrowParentsNotVisited.foreach { parent =>
      ancestors.add(parent)
      visit(parent)
    }
  }

  visit(this)

  // In case there is a cycle, do not include the root itself
  ancestors.filterNot(_ == this).toSeq
}

方法的返回值为Seq类型,里面包含RDD,这不难理解,一个RDD他是窄依赖,那么他有一个父RDD,但是你不能保证他的父RDD没有依赖,所以返回结果为一个序列,里面装有很多的RDD。然后一开始我们看到一个HashSet,他的里面装有RDD(不过目前是空的),为什么用HashSet?好问题,因为它里面没有重复,我们不需要获取一个含有重复的序列(就像别人问你家里都有谁,你不会说两次爸爸一样)。下面定义了一个方法visit,首先这里很有意思,因为我们在一个方法里面定义了另外一个方法,所以你不得不佩服scala的灵活性。

这个visit方法需要传入一个RDD作为参数。然后我们进入方法第一行,rdd调用他的dependencies方法,以下是dependencies方法:


/**
 * Get the list of dependencies of this RDD, taking into account whether the
 * RDD is checkpointed or not.
 */
final def dependencies: Seq[Dependency[_]] = {
  checkpointRDD.map(r => List(new OneToOneDependency(r))).getOrElse {
    if (dependencies_ == null) {
      dependencies_ = getDependencies
    }
    dependencies_
  }
}

这个方法同样返回一个Seq里面是Dependecy的类型,Dependency类型是一个抽象类,很简单,整体如下:


abstract class Dependency[T] extends Serializable {
  def rdd: RDD[T]
}

只有一个RDD,那么回到上面dependencies方法中,第一行,我们查看checkpointRDD。可能有很多人不知道这是什么,那么请查看如下代码:


/** An Option holding our checkpoint RDD, if we are checkpointed */
private def checkpointRDD: Option[CheckpointRDD[T]] = checkpointData.flatMap(_.checkpointRDD)

可以看到,checkpointRDD是一个Option类型里面包含的RDD是我们已经checkpointed的。我的理解是,这句可以理解为一个过滤过程。回到上面的dependencies方法中,我们看到checkpointRDD对其内的每个元素(也就是CheckpointRDD类型的RDD)执行方法OneToOneDependency(),代码如下:


/**
 * :: DeveloperApi ::
 * Represents a one-to-one dependency between partitions of the parent and child RDDs.
 */
@DeveloperApi
class OneToOneDependency[T](rdd: RDD[T]) extends NarrowDependency[T](rdd) {
  override def getParents(partitionId: Int): List[Int] = List(partitionId)
}

这里为不引起混乱,不在进入更深的方法,这个方法的含义是获取所有的子RDD与父RDD的对应关系为1对1的父RDD的分区号。回到denpendencies方法中,我们看到下面有个方法getDependencies顾名思义,获取所有的依赖(此处方法不在深入),此时dependencies方法返回结果,返回一开始的visit方法中,为了方便查看,我再打印出来一遍:


def visit(rdd: RDD[_]) {
  val narrowDependencies = rdd.dependencies.filter(_.isInstanceOf[NarrowDependency[_]])
  val narrowParents = narrowDependencies.map(_.rdd)
  val narrowParentsNotVisited = narrowParents.filterNot(ancestors.contains)
  narrowParentsNotVisited.foreach { parent =>
    ancestors.add(parent)
    visit(parent)
  }
}

获取到值进行filter过滤,过滤的条件是查看里面的RDD是否为NarrowDependency类型。然后我们将过滤后的值保存变量名字为:narrowDependencies,然后将依赖中的rdd取出保存变量名字为narrowParents,也就是所谓的父RDD。然后下面几段代码的目的是把父RDD放入到我们一开始创建的HashSet中,重复的去掉,没有的加入。

再回到getNarrowAncestors方法中,我们看到下面它调用了visit方法,把本身作为参数传入。最后那一句是为了过滤出父类中又包含自身RDD这种循环而存在的,把过滤后的转化为Seq作为结果传出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值