在select
上使用DataFrame
来获取我们需要的信息并将基础RDD的每一行映射到同一目的之间是否存在“机械”差异?
“机械”我指的是执行操作的机制。换句话说,实施细节。
哪两个提供更好/更高效?
df = # create dataframe ...
df.select("col1", "col2", ...)
或
df = # create dataframe ...
df.rdd.map(lambda row: (row[0], row[1], ...))
我正在进行性能测试,因此我将找出哪个更快但我想知道实现差异和优缺点。
答案 0 :(得分:2)
RDD只是转换和行动的图谱。
DataFrame有一个逻辑计划,在执行操作之前由Catalyst逻辑查询优化器进行内部优化。
在你的情况下意味着什么?
如果你有DataFrame,那么你应该使用select
- 任何额外的工作,如过滤,加入等,都将得到优化。优化的DataFrame可以比普通RDD快10倍。换句话说,在执行select
之前,Spark会尝试更快地进行查询。使用dataFrame.rdd.map()
另外一个:rdd
值通过以下方式懒散计算:
lazy val rdd: RDD[T] = {
val objectType = exprEnc.deserializer.dataType
val deserialized = CatalystSerde.deserialize[T](logicalPlan)
sparkSession.sessionState.executePlan(deserialized).toRdd.mapPartitions { rows =>
rows.map(_.get(0, objectType).asInstanceOf[T])
}
}
所以Spark会使用它的RDD,地图和演员内容。两个版本的DAG在查询中几乎相同,如此问题,因此性能将类似。然而,在更高级的情况下,使用数据集的好处将非常明显,正如Spark PMCs在Databricks博客上所写,经过Catalyst优化后数据集甚至可以快100倍
请注意,DataFrame = Dataset [Row]并且它在后台使用RDD - 但RDD的图形是在优化后创建的
注意:Spark正在统一API。 Spark ML现在以DataFrame为中心,不应使用旧API。流媒体正在转向结构化流媒体。因此,即使您的性能不会提高很多,也可以考虑使用DataFrames。这对未来的发展会有更好的决定,当然比使用普通的RDD更快
答案 1 :(得分:1)
在这个带有DataFrame.select
和DataFrame.rdd.map
的简化示例中,我认为差异可能几乎可以忽略不计。
毕竟你已经加载了你的数据集,只做了投影。最终,两者都必须从Spark的InternalRow
列式格式反序列化数据,以计算操作的结果。
您可以通过DataFrame.select
检查explain(extended = true)
会发生什么情况,在那里您将了解实际计划(以及实际计划)。
scala> spark.version
res4: String = 2.1.0-SNAPSHOT
scala> spark.range(5).select('id).explain(extended = true)
== Parsed Logical Plan ==
'Project [unresolvedalias('id, None)]
+- Range (0, 5, step=1, splits=Some(4))
== Analyzed Logical Plan ==
id: bigint
Project [id#17L]
+- Range (0, 5, step=1, splits=Some(4))
== Optimized Logical Plan ==
Range (0, 5, step=1, splits=Some(4))
== Physical Plan ==
*Range (0, 5, step=1, splits=Some(4))
将物理计划(即SparkPlan
)与您使用rdd.map
(toDebugString
)所做的事情进行比较,您就会知道什么可能“更好”。
scala> spark.range(5).rdd.toDebugString
res5: String =
(4) MapPartitionsRDD[8] at rdd at <console>:24 []
| MapPartitionsRDD[7] at rdd at <console>:24 []
| MapPartitionsRDD[6] at rdd at <console>:24 []
| MapPartitionsRDD[5] at rdd at <console>:24 []
| ParallelCollectionRDD[4] at rdd at <console>:24 []
(再次在这个人为的例子中,我认为没有赢家 - 两者都尽可能高效。)
请注意,DataFrame
实际上是Dataset[Row]
,它使用RowEncoder
将数据编码(即序列化)为InternalRow
列二进制格式。如果您要在管道中执行更多运算符,那么仅仅因为低级别的幕后逻辑查询计划优化和柱状二进制文件,您可以通过坚持Dataset
而不是RDD
获得更好的性能格式。
有很多优化措施,试图击败它们可能会浪费你的时间。你必须全心全意地了解Spark内部以获得更好的性能(价格肯定是可读性)。
有很多内容,我强烈建议观看Herman van Hovell的谈话A Deep Dive into the Catalyst Optimizer,以了解和欣赏所有的优化。
我对它的看法是...... “远离RDD,除非你知道你在做什么”。