Spark 一:从实践入手

153 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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()