持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
Spark 被我们叫做所谓的计算引擎。在开始阶段,我们不过多的触及这个概念,仅仅把它当作一个服务器。用于按照我们指定的流程做某件事。从这个角度,可以把它当作一个C/S模式的软件。
首先介绍两种不同的Spark对数据的抽象描述。
一是RDD,它可以被认为是一个列表。我们可以在他之上执行一些函数式语言在列表上的操作。或者说类似于Java在stream上的操作。
二是DataFrame,它可以被认为是一个表格。类似与Pandas的DataFrame。同时功能上也等价于SQL的表格。
从一个实例入手。
求商品销量的平均值。
假设有多种商品,每种商品有多个销售记录。 类似于 (A,2)、(A,3)、(A,4)、(B,2)、(B,3)、(B,4)、(C,2)、(C,3)、(C,4)。
基于RDD 处理
第一步是要把数据转换为RDD或者说是列表。具体是文件还是什么需要具体分析,这里就不过多涉及了。总而言之需要将数据转换为RDD。并且类型是二元组Tuple[String,Int]。这样方便我们后续处理。
在Spark中,二元组的第一个元素会被当作key,第二个元素会被当作value。 因此我们只需要按照key分组,并求解每组的平均值。
但是注意到我们需要计算平均值,如果是单纯求和,长度信息会丢失,因此可以尝试增加一个部分,记录长度。最后求和的时候,顺便求出长度。
具体来说代码如下:
rdd.mapValues((_,1)).reduceByKey((x,y) => (x._1 + y._1, x._2 + y._2)).mapValues(x => x._1.toDouble / x._2).collect()
如果不考虑纯分组时携带key造成的额外开销,也可以这么写:
rdd.groupByKey().mapValues(x => x.sum.toDouble / x.size).collect()
第二个性能其实没有第一个好。但是因为自带了长度和求和函数,所以看起来会更简洁。
基于DataFrame 处理
基于DataFrame 如果是直接读取文件会更简单一些,如果需要基于RDD创建可以借助scala的隐式转换,或者是转换成Row类型创建。
处理上我们说过,等效于SQL的表格。
因此可以创建SQL表,使用SQL语句处理
df.createOrReplaceTempView("wordcount")
sparkSession.sql("select name, avg(count) from wordcount group by name")
SQL对这种简单的聚合天然友好。
基于DataFrame自身API也是可行的。
val averages2 = df.groupBy("name").avg("count")
averages2.show()