评估数据帧是否为空的方法之一是df.rdd.isEmpty()
,但是,我在sparkUI执行中看到rdd at mycode.scala:123
。这让我想知道这个rdd()函数实际上是一个动作而不是转换。
我知道isEmpty()是一个动作,但我确实看到了isEmpty() at mycode.scala:234
的单独阶段,所以我认为它们是不同的动作?
答案 0 :(得分:3)
rdd
以表示“RDD术语”中的结构化查询,以便Spark可以执行它。它是您的类型T
的JVM对象的RDD。如果使用不当可能会导致内存问题,因为:
将位于JVM外部的内部管理优化行传输到JVM中的内存空间
将二进制行转换为业务对象(JVM“true”表示)
第一个将增加计算所需的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 返回数据集中的行数。
如果您坚持要求相当低级别来检查数据集是否为空,我宁愿使用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
表示Dataset
的内容为(懒惰创建的)RDD
,其中包含JVM类型T
的行。
rdd: RDD[T]
正如您在source code(上面粘贴)中所看到的,最后请求rdd
将触发一次额外的计算以获得RDD。