将数据框投射到数据集后 select 是否优化?

Is a select after casting a data frame to dataset optimized?

我有以下场景:

 case class A(name:String,age:Int)
 val df = List(A("s",2)).toDF
 df.write.parquet("filePath")
 val result = spark.read.parquet("filePath").as[A].select("age")

以上是否仅针对 select age 进行了优化?看到 result.explain 我看到以下

'Project [unresolvedalias('age, None)]
+- Relation[name#48,age#49] parquet

== Analyzed Logical Plan ==
age: int
Project [age#49]
+- Relation[name#48,age#49] parquet

== Optimized Logical Plan ==
Project [age#49]
+- Relation[name#48,age#49] parquet

== Physical Plan ==
*(1) FileScan parquet [age#49] Batched: true, Format: Parquet, Location:    InMemoryFileIndex[file:/Volumes/Unix/workplace/Reconciliation/src/TFSReconciliationCore/~/Downloa..., PartitionFilters: [], PushedFilters: [], ReadSchema: struct<age:int>

好像只读了age。但是 as 有什么用呢?我阅读物理计划是否正确?

是的,你没看错。 Parquet 文件有两列 - nameage:

 Relation[name#48,age#49] parquet

但实际上只有age会被读取:

 Project [age#49]

But then what purpose does as serve?

对于像上面那样的优化,Spark 需要创建一个内部模式。

在某些情况下,例如 parquet 文件,我们有一个页脚包含带有架构的元数据,但默认情况下 Spark 必须读取所有页脚以合并(可能)不同的架构。
在其他情况下(csvjson 等),如果用户不提供模式,Spark 需要扫描数据并创建它。

我们还需要一些通用容器来访问这些值,我们有一个名为 Row 的容器。

Row is a generic row object with an ordered collection of fields that can be accessed by an ordinal / an index (aka generic access by ordinal), a name (aka native primitive access) or using Scala's pattern matching.

在您的示例中,编写以下代码完全没问题:

spark.read.parquet("filePath").select("age")

读取方法returnsDataframe,其实就是一个Dataset of Rows.
当我们使用 as 时,我们将 Dataset[Row] 转换为 Dataset[A],其中 A 几乎可以是任何情况 - class。

在我看来,它使代码更清晰、更易读。使用类似 SQL 的方法并没有太大区别,但是当我们需要添加 map/flatMap 或自定义聚合时,代码将变得更容易理解。