结构化API是处理各种数据类型的工具,可处理非结构化的日志文件、半结构化的CSV文件,以及高度结构化的Parquet文件
结构化API指的是以下三种核心分布式集合类型的API:
Dataset类型
DataFrame类型
SQL表和视图
Dataset类型和DataFrame类型
Dataset和DataFrame是具有行和列的类似于(分布式)数据表的集合类型,所有列的行数相同(可以使用null来指定缺省值),每一列的所有行都必须保持一致的数据类型。Spark中的DataFrame和Dataset代表不可变的数据集合,可以通过它指定对特定位置数据的操作,该操作将以惰性评估方式执行。当对DataFrame执行动作操作时,将触发Spark执行具体转换操作并返回结果,这些代表了如何操纵行和列来计算出用户期望结果的执行计划。
Schema
Schema数据模式定义了该分布式集合中存储的数据类型
Schema定义了DataFrame的列名和类型,可以手动定义或者从数据源读取模式(通常定义为模式读取)
DataFrame与Dataset的比较
实质上,结构化API包含两类API,即非类型化的DataFrame和类型化的Dataset,说DataFrame是无类型的可能不太准确,因为它们其实是有类型的,只是Spark完全负责维护它们的类型,仅在运行时检查这些类型是否与schema中指定的类型一致。与之相对应的,Dataset在编译时就会检查类型是否符合规范。Dataset仅适用于基于Java虚拟机的语言(比如Scala和Java),并通过case类或Java beans指定类型
在大多数情况下,你会使用DataFrame。在Scala版本的Spark中,DataFrame就是一些Row类型的Dataset集合。"Row"类型是Spark用于支持内存计算而优化的数据格式。这种格式有利于高效计算,因为它避免使用会带来昂贵垃圾回收开销和对象实例化开销的JVM类型,而是基于自己的内部格式运行,所以并不会产生这种开销。Python版本和R语言版本的Spark并不支持Dataset,所有东西都是DataFrame,这样我们就可以使用这种优化的数据格式进行计算处理。
列
列表示一个简单类型(例如,整数或字符串),或者一个复杂类型,或者空值null。Spark记录所有这些类型的信息并提供多种转换方法,简单来说,可以将Spark列想象为一个数据表的列即可
行
一行对应一个数据记录,正如我们在下面对DataFrame调用collect方法时所看到的,DataFrame中的每条记录都必须是Row类型。我们可以通过SQL手动创建、或者从弹性分布式数据集(RDD)提取,或从数据源手动创建这些行。
结构化API执行概述
- 编写DataFrame / Dataset / SQL代码
- 如果代码能有效执行,Spark将其转换为一个逻辑执行计划(Logical Plan)
- Spark将此逻辑执行计划转化为一个物理执行计划,检查可行的优化策略,并在此过程中检查优化
- Spark在集群上执行该物理执行计划(RDD操作)
我们编写的代码通过控制台提交给Spark,或者以一个Spark作业的形式提交。然后代码将交由Catalyst优化器决定如何执行,并制定一个执行计划。最后代码被运行,得到的结果返回给用户。
逻辑计划
执行的第一阶段旨在获取用户代码并将其转换为逻辑计划。
这个逻辑计划仅代表一组抽象转换,并不涉及执行器或驱动器,它只是将用户的表达式集合转换为最优的版本。它通过将用户代码转换为未解析的逻辑计划来实现这一点。这个计划没有解析,因为虽然你的代码可能是有效的,但它引用的表可能不存在。Spark使用catalog(所有表和DataFrame信息的存储库)在分析器中解析列和表格。如果目录中不存在所需的表或列名称,分析器可能会拒绝该未解析的逻辑计划。如果分析器可以解析它,结果将通过Catalyst优化器,Catalyst优化器尝试通过下推谓词或选择操作来优化逻辑计划。用户也可以扩展Catalyst优化器来支持自己的特定领域优化策略。
物理计划
在成功创建优化的逻辑计划后,Spark开始执行物理计划流程。物理计划(通常称为Spark计划)通过生成不同的物理执行策略,并通过代价模型进行比较分析,从而指定如何在集群上执行逻辑计划。例如执行一个连接操作就会涉及代价比较,它通过分析数据表的物理属性(表的大小或分区的大小),对不同的物理执行策略进行代价比较,选择合适的物理执行计划。物理执行计划产生一系列的RDD和转换操作,这就是Spark被称为编译器的原因,因为它将对DataFrame,Dataset和SQL中的查询操作为你编译一系列RDD的转换操作
执行
在选择一个物理计划时,Spark将所有代码运行在Spark的底层编程接口RDD上。Spark在运行时执行进一步优化,生成可以在执行期间优化任务或阶段的本地Java字节码,最终将结果返回给用户