为什么RDD.foreach失败" SparkException:这个RDD缺少SparkContext"?

时间:2017-05-20 16:08:00

标签: scala apache-spark rdd

我有一个数据集(作为RDD),我使用不同的filter运算符将其划分为4个RDD。

 val RSet = datasetRdd.
   flatMap(x => RSetForAttr(x, alLevel, hieDict)).
   map(x => (x, 1)).
   reduceByKey((x, y) => x + y)
 val Rp:RDD[(String, Int)] = RSet.filter(x => x._1.split(",")(0).equals("Rp"))
 val Rc:RDD[(String, Int)] = RSet.filter(x => x._1.split(",")(0).equals("Rc"))
 val RpSv:RDD[(String, Int)] = RSet.filter(x => x._1.split(",")(0).equals("RpSv"))
 val RcSv:RDD[(String, Int)] = RSet.filter(x => x._1.split(",")(0).equals("RcSv"))

我已将RpRpSV发送到以下函数calculateEntropy

def calculateEntropy(Rx: RDD[(String, Int)], RxSv: RDD[(String, Int)]): Map[Int, Map[String, Double]] = {
        RxSv.foreach{item => {
               val string = item._1.split(",")
               val t = Rx.filter(x => x._1.split(",")(2).equals(string(2)))

        .
        .
    }
}

我有两个问题:

1-当我在RxSv上循环操作时:

RxSv.foreach{item=> { ... }}

它会收集分区的所有项目,但我只想要一个我所在的分区。如果您说用户map有效,但我不会在RDD上进行任何更改。

因此,当我在具有4个工作线程和驱动程序的集群上运行代码时,数据集将分为4个分区,每个工作程序都会运行代码。但是例如我使用foreach循环,因为我在代码中指定。司机收集工人的所有数据。

2-我遇到过这段代码的问题

val t = Rx.filter(x => x._1.split(",")(2).equals(abc(2)))

错误:

org.apache.spark.SparkException: This RDD lacks a SparkContext.


在下列情况下可能发生:

(1)RDD transformationsactions不是由驱动程序调用,而是在其他转换中调用;
例如,rdd1.map(x => rdd2.values.count() * x)无效,因为无法在transformation count内执行值actionrdd1.map transformation。有关更多信息,请参阅SPARK-5063。

(2)当Spark Streaming作业从检查点恢复时,如果在RDD操作中使用了未由流作业定义的DStream的引用,则会触发此异常。有关详细信息,请参阅SPARK-13​​758。

1 个答案:

答案 0 :(得分:3)

首先,我强烈建议使用cache运算符缓存第一个RDD。

RSet.cache

每次filter用于其他RDD时,这将避免扫描和转换数据集:RpRcRpSvRcSv

引用cache的scaladoc:

  

cache()使用默认存储级别(MEMORY_ONLY)保留此RDD。

性能应该提高。

其次,我要非常小心地使用术语"分区"引用过滤的RDD,因为该术语在Spark中具有特殊含义。

分区说明Spark为一个动作执行了多少任务。它们是Spark的提示,因此Spark(Spark开发人员)可以对您的分布式管道进行微调。

管道分布在群集节点上,每个分区方案都有一个或多个Spark执行器。如果您决定在RDD中拥有一个分区,那么在对该RDD执行操作后,您将在一个执行程序上执行一个任务。

filter转换不会更改分区数(换句话说,它会保留分区)。分区数,即任务数,正是RSet的分区数。

  

1-当我在RxSv上循环操作时,它会收集分区的所有项目,但我只想要一个我所在的分区

你是。不要担心它,因为Spark将在数据存在的执行器上执行任务。 foreach是一个执行收集项目的操作,但描述了在执行程序上运行的计算,其中数据分布在整个群集中(作为分区)。

如果您希望每个分区一次处理所有项目,请使用foreachPartition

  

foreachPartition 将函数f应用于此RDD的每个分区。

  

2-我遇到过这段代码的问题

在以下代码行中:

    RxSv.foreach{item => {
           val string = item._1.split(",")
           val t = Rx.filter(x => x._1.split(",")(2).equals(string(2)))

您正在执行foreach操作,该操作又使用Rx RDD[(String, Int)]。这是不允许的(如果有可能不应该编译)。

行为的原因是RDD是一种数据结构,它只描述了在执行操作并且存在于驱动程序(协调器)上时数据集发生的情况。驱动程序使用数据结构来跟踪数据源,转换和分区数。

当驱动程序在执行程序上生成任务时,RDD作为一个实体消失了(=消失)。

当任务运行时,没有任何东西可以帮助他们知道如何运行属于他们工作的RDD。因而错误。 Spark对此非常谨慎,并在执行任务后可能导致问题之前检查此类异常情况。