Performance 为什么要使用DataFrame.select而不是DataFrame.rdd.map(反之亦然)?
在Performance 为什么要使用DataFrame.select而不是DataFrame.rdd.map(反之亦然)?,performance,apache-spark,dataframe,apache-spark-sql,rdd,Performance,Apache Spark,Dataframe,Apache Spark Sql,Rdd,在DataFrame上使用select来获取我们需要的信息和出于相同目的映射底层RDD的每一行之间是否存在“机械”区别 通过“机械”我指的是执行操作的机制。换句话说,实现细节 提供的两个选项中,哪一个性能更好/更高 df = # create dataframe ... df.select("col1", "col2", ...) 或 我在性能测试的中间,所以我要找出哪个更快,但我想知道什么是实现差异和优点/缺点。 < P> RDD只是一个转换和动作的图形谱系。 数据帧具有一个逻辑计划,该计划
DataFrame
上使用select
来获取我们需要的信息和出于相同目的映射底层RDD的每一行之间是否存在“机械”区别
通过“机械”我指的是执行操作的机制。换句话说,实现细节
提供的两个选项中,哪一个性能更好/更高
df = # create dataframe ...
df.select("col1", "col2", ...)
或
我在性能测试的中间,所以我要找出哪个更快,但我想知道什么是实现差异和优点/缺点。
< P> RDD只是一个转换和动作的图形谱系。 数据帧具有一个逻辑计划,该计划在执行操作之前由Catalyst逻辑查询优化器进行内部优化 这对你来说意味着什么 如果您有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现在以数据帧为中心,不应使用旧API。流媒体正在向结构化流媒体转变。因此,即使在您的情况下不会有太多的性能改进,也可以考虑使用DATAFRAM。对于未来的开发,这将是一个更好的决策,当然比在这个过于简单的示例中使用普通RDD要快。选择和DataFrame.RDD.map,我认为差异几乎可以忽略不计 毕竟,您已经加载了数据集,并且只执行投影。最终,两者都必须从Spark的
InternalRow
列格式反序列化数据,以计算操作的结果
您可以通过DataFrame检查发生了什么。通过explain(extended=true)
选择
将物理计划(即SparkPlan
)与您正在使用rdd.map
(通过toDebugString
)执行的操作进行比较,您将知道什么可能“更好”
scala>spark.range(5).rdd.toDebugString
res5:字符串=
(4) 在rdd处的MapPartitionsRDD[8]在:24[]
|rdd处的MapPartitionsRDD[7]位于:24[]
|rdd处的MapPartitionsRDD[6]位于:24[]
|rdd处的MapPartitionsRDD[5]位于:24[]
|在rdd处的并行采集rdd[4]在:24[]
(同样在这个人为的例子中,我认为没有赢家——两者都尽可能有效)
请注意,DataFrame
实际上是一个Dataset[Row]
,它使用rowcoder
将数据编码(即序列化)为InternalRow
列二进制格式。如果要在管道中执行更多的运算符,那么使用Dataset
比使用RDD
可以获得更好的性能,这仅仅是因为低级别的幕后逻辑查询计划优化和列式二进制格式
有很多优化,试图打败它们往往会浪费您的时间。为了获得更好的性能,您必须背诵Spark的内部结构(价格当然是可读性)
这其中有很多,我强烈建议观看赫尔曼·范·霍维尔的演讲,了解并欣赏所有的优化
我的看法是……“除非你知道自己在做什么,否则不要使用rdd”。总之,dataframe
操作总是比rdd
对应操作快。@mtoto我不太确定。数据集必须序列化和反序列化数据,以便在类型化操作中处理+Scala代码,UDF可能会使优化消失(这对RDD来说并不“危险”。@JacekLaskowski数据集中的坏代码可能比RDD中的好代码慢;)然而,Dataset比普通RDD更快的次数更多。请注意Spark、ML和Streaming中的新API将以数据集为中心
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])
}
}
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))
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 []