本文讲述spark sql中的dataset的组成部分,并对其创建过程进行分析。
我们可以总结出dataset的一些要点,如下:
数据集(Dataset)是具有强类型的某个特定域的对象的集合,该集合可以使用函数或关系运算并行转换(transformed)。 每个数据集还有一个称为“DataFrame”的无类型视图,它是数据集行([Row])的集合。
和RDD一样,基于数据集的操作可以分为转换(transformations)和行动(action)。transformations操作会产生新的数据集(可能有多种不同类型),action操作会触发计算任务并返回结果。
transformations包括:map,filter,select和aggregate(groupBy
)。
action包括:count, show或写入文件系统。
数据集是"懒惰的"(lazy),即只有在调用action的函数时才会触发计算。在数据集的内部实现层面,一个数据集表示一个逻辑计划(logic plan),该逻辑计划描述产生数据该如何计算。
当调用action操作函数时,Spark的查询优化器(query optimizer)会优化逻辑计划,并按并行(parallel)和分布式(distributed)的方式生成有效执行的物理计划(physical plan)。可以通过explain函数来探查逻辑计划和优化的物理计划。
为了更加有效的支持某个特域的对象,需要使用编码器([Encoder])。编码器将域特定类型T
映射到Spark的内部类型系统。例如,给定一个带有两个字段的Person
,name
(string)和age
(int),编码器用于告诉Spark在运行时生成代码以将Person
对象序列化为二进制结构。该二进制结构通常占用更少的内存和对数据处理(例如,柱状格式)的效率进行优化。要了解数据的内部二进制表示,可以使用schema
函数。
注意:以上文字基本上都来自于Dataset类的实现代码注释。
dataset的大致执行过程如下图所示:
注意,该图是dataset执行的一个概要,其中省略了一些比较细节的环节。
可见,dataset的执行过程大体分为三个大的阶段,一个是逻辑执行计划(logic plan)的创建和分析及其优化阶段,一个是物理执行计划(physical plan)的创建和优化,最后一个是形成rdd和并执行任务的阶段。从图中可以大体看出这几个阶段。
通过对dataset的queryExecution参数,我们可以打印出dataset的逻辑执行计划,如下:
scala> dada.sort(col("_c0")).queryExecution
res4: org.apache.spark.sql.execution.QueryExecution =
== Parsed Logical Plan ==
'Sort ['_c0 ASC NULLS FIRST], true
+- Relation[_c0#12,_c1#13,_c2#14,_c3#15,_c4#16] csv
== Analyzed Logical Plan ==
_c0: string, _c1: string, _c2: string, _c3: string, _c4: string
Sort [_c0#12 ASC NULLS FIRST], true
+- Relation[_c0#12,_c1#13,_c2#14,_c3#15,_c4#16] csv
== Optimized Logical Plan ==
Sort [_c0#12 ASC NULLS FIRST], true
+- Relation[_c0#12,_c1#13,_c2#14,_c3#15,_c4#16] csv
== Physical Plan ==
*Sort [_c0#12 ASC NULLS FIRST], true, 0
+- Exchange rangepartitioning(_c0#12 ASC NULLS FIRST, 200)
+- *FileScan csv [_c0#12,_c1#13,_c2#14,_c3#15,_c4#16] Batched: false, Format: CSV, Location: InMemoryFileIndex[hdfs://localhost:9000/curdata/idtimedata], PartitionFilters: [], PushedFilters: [], ReadSchema: s...
说明:其中的_c0是加载的数据没有Header而产生的列名。
另外,也可以通过explain函数,来打印逻辑和物理执行计划。
本文讲述了spark dataset的实现原理的概要。并介绍了dataset的操作实现的大致流程。