这是在RDD上实现懒惰`take`的合适方法吗?

时间:2016-12-01 18:48:27

标签: scala apache-spark

非常不幸的是take RDD是一个严格的操作而不是懒惰但是我不知道为什么我认为这是一个令人遗憾的设计在这里和现在

我的问题是,这是否适用于take的惰性RDD。它似乎有效,但我可能会错过一些非显而易见的问题。

def takeRDD[T: scala.reflect.ClassTag](rdd: RDD[T], num: Long): RDD[T] =
  new RDD[T](rdd.context, List(new OneToOneDependency(rdd))) {
    // An unfortunate consequence of the way the RDD AST is designed
    var doneSoFar = 0L

    def isDone = doneSoFar >= num

    override def getPartitions: Array[Partition] = rdd.partitions

    // Should I do this? Doesn't look like I need to
    // override val partitioner = self.partitioner

    override def compute(split: Partition, ctx: TaskContext): Iterator[T] = new Iterator[T] {
      val inner = rdd.compute(split, ctx)

      override def hasNext: Boolean = !isDone && inner.hasNext

      override def next: T = {
        doneSoFar += 1
        inner.next
      }
    }
  }

1 个答案:

答案 0 :(得分:3)

回答你的问题

不,这不起作用。无法在Spark群集中同时查看和更新​​变量,而这正是您尝试使用doneSoFar的原因。如果您尝试这样做,那么当您运行compute(并行地跨多个节点)时,您

a)在任务中序列化takeRDD,因为你引用了类变量doneSoFar。这意味着您将该类写入字节并在每个JVM(执行程序)中创建一个新实例

b)更新计算中的doneSoFar,它更新每个执行程序JVM上的本地实例。您将从每个分区中获取等于num的多个元素。

由于某些JVM属性,它可能在Spark本地模式下工作,但在集群模式下运行Spark时,它只能起作用。

为什么take是一个动作,而不是转换

RDD是分布式的,因此对精确数量的元素进行子集化是一种效率低下的操作 - 它不能完全并行完成,因为每个分片都需要有关其他分片的信息(比如是否应该计算)在所有)。 Take非常适合将分布式数据带回本地内存。

rdd.sample是一种类似于分布式世界的操作,可以轻松并行运行。