RDD困境
map、filter,它们都需要一个辅助函数 f 来作为形参,通过调用 map(f)、filter(f) 才能完成计算。以 map 为例,我们需要函数 f 来明确对哪些字段做映射,以什么规则映射。filter 也一样,我们需要函数 f 来指明以什么条件在哪些字段上过滤。这样一来,Spark 只知道开发者要做 map、filter,但并不知道开发者打算怎么做 map 和 filter。换句话说,对于 Spark 来说,辅助函数 f 是透明的。在 RDD 的开发框架下,Spark Core 只知道开发者要“做什么”,而不知道“怎么做”。
在 RDD 开发框架下,Spark Core 的优化空间受限。绝大多数 RDD 高阶算子所封装的封装的计算逻辑(形参函数 f)对于 Spark Core 是透明的,Spark Core 除了用闭包的方式把函数 f 分发到 Executors 以外,没什么优化余地。而这,就是 RDD 之殇
DataFrame
比 RDD,DataFrame 到底有何不同呢?我们不妨从两个方面来对比它们的不同:一个是数据的表示形式(Data Representation),另一个是开发算子。
DataFrame 与 RDD 一样,都是用来封装分布式数据集的。但在数据表示方面就不一样了,DataFrame 是携带数据模式(Data Schema)的结构化数据,而 RDD 是不携带 Schema 的分布式数据集。
恰恰是因为有了 Schema 提供明确的类型信息,Spark 才能耳聪目明,有针对性地设计出更紧凑的数据结构,从而大幅度提升数据存储与访问效率。在开发 API 方面,RDD 算子多采用高阶函数,高阶函数的优势在于表达能力强,它允许开发者灵活地设计并实现业务逻辑。
而 DataFrame 的表达能力却很弱,它定义了一套 DSL 算子(Domain Specific Language),如我们上一节课用到的 select、filter、agg、groupBy,等等,它们都属于 DSL 算子。
DSL 语言往往是为了解决某一类特定任务而设计,非图灵完备,因此在表达能力方面非常有限。DataFrame 的算子大多数都是标量函数(Scalar Functions),它们的形参往往是结构化二维表的数据列(Columns)。
尽管 DataFrame 算子在表达能力方面更弱,但是 DataFrame 每一个算子的计算逻辑都是确定的,比如 select 用于提取某些字段,groupBy 用于对数据做分组,等等。这
些计算逻辑对 Spark 来说,不再是透明的,因此,Spark 可以基于启发式的规则或策略,甚至是动态的运行时信息,去优化 DataFrame 的计算过程。总结下来,相比 RDD,DataFrame 通过携带明确类型信息的 Schema、以及计算逻辑明确的转换算子,为 Spark 引擎的内核优化打开了全新的空间。