Dataset.rdd是一个动作还是转换?

时间:2017-06-22 20:16:55

标签: apache-spark apache-spark-sql

评估数据帧是否为空的方法之一是df.rdd.isEmpty(),但是,我在sparkUI执行中看到rdd at mycode.scala:123。这让我想知道这个rdd()函数实际上是一个动作而不是转换。

我知道isEmpty()是一个动作,但我确实看到了isEmpty() at mycode.scala:234的单独阶段,所以我认为它们是不同的动作?

1 个答案:

答案 0 :(得分:3)

生成

rdd以表示“RDD术语”中的结构化查询,以便Spark可以执行它。它是您的类型T的JVM对象的RDD。如果使用不当可能会导致内存问题,因为:

  1. 将位于JVM外部的内部管理优化行传输到JVM中的内存空间

  2. 将二进制行转换为业务对象(JVM“true”表示)

  3. 第一个将增加计算所需的JVM内存,而后者是额外的转换步骤。

    对于计算行数的这种简单计算,您宁愿坚持count作为优化且相当便宜的计算(可以避免复制对象和应用模式)。

    在内部,Dataset会在InternalRow中保留行。这会降低Spark应用程序的JVM内存需求。计算RDD(来自rdd)以表示执行Spark操作后执行的Spark转换。

    请注意,执行rdd会创建一个RDD,并且也需要进行一些计算。

    所以,是的,rdd可能被视为一个动作,因为它“执行”了查询(即后面的数据集的物理计划),但最后它只给出RDD (因此它不能是定义的动作,因为Spark动作返回非RDD值)。

    正如您在code中所看到的那样:

      lazy val rdd: RDD[T] = {
        val objectType = exprEnc.deserializer.dataType
        val deserialized = CatalystSerde.deserialize[T](logicalPlan) // <-- HERE see explanation below
        sparkSession.sessionState.executePlan(deserialized).toRdd.mapPartitions { rows =>
          rows.map(_.get(0, objectType).asInstanceOf[T])
        }
      }
    

    rdd懒惰地计算,只计算一次。

      

    评估数据帧是否为空的方法之一是执行df.rdd.isEmpty()

    我想知道你在哪里找到它。我只是count

      

    count():Long 返回数据集中的行数。

    toRdd Lazy Value

    如果您坚持要求相当低级别来检查数据集是否为空,我宁愿使用Dataset.queryExecution.toRdd。这几乎就像rdd没有额外的复制和应用模式。

    df.queryExecution.toRdd.isEmpty
    

    比较以下RDD血统,并考虑哪些似乎更好。

    val dataset = spark.range(5).withColumn("group", 'id % 2)
    scala> dataset.rdd.toDebugString
    res1: String =
    (8) MapPartitionsRDD[8] at rdd at <console>:26 [] // <-- extra deserialization step
     |  MapPartitionsRDD[7] at rdd at <console>:26 []
     |  MapPartitionsRDD[6] at rdd at <console>:26 []
     |  MapPartitionsRDD[5] at rdd at <console>:26 []
     |  ParallelCollectionRDD[4] at rdd at <console>:26 []
    
    // Compare with a more memory-optimized alternative
    // Avoids copies and has no schema
    scala> dataset.queryExecution.toRdd.toDebugString
    res2: String =
    (8) MapPartitionsRDD[11] at toRdd at <console>:26 []
    |  MapPartitionsRDD[10] at toRdd at <console>:26 []
    |  ParallelCollectionRDD[9] at toRdd at <console>:26 []
    

    从Spark的角度来看,转换相当便宜,因为它们不会导致任何混乱,但考虑到计算之间的内存需求变化,我会使用后者(使用toRdd)。

    rdd Lazy Value

    rdd表示Dataset的内容为(懒惰创建的)RDD,其中包含JVM类型T的行。

    rdd: RDD[T]
    

    正如您在source code(上面粘贴)中所看到的,最后请求rdd将触发一次额外的计算以获得RDD。

    1. 创建一个新的逻辑计划来反序列化数据集的逻辑计划,即从JVM外部管理的内部二进制行格​​式获得额外的反序列化,作为生活在内部 JVM中的JVM对象(想想GC,你应该不惜一切代价避免)