1.结构化API概述
Apache Spark是一个用于大规模数据处理的快速,可靠,容错的分布式计算框架。
Spark有两套基本的API(Application Programming Interface,应用程序编程接口):
- 低级的“非结构化”API:弹性分布式数据集(RDD)
- 高级的“结构化”API:类型安全的结构化数据API——Dataset
结构化API是处理各种数据类型的工具,可处理非结构化的日志文件,半结构化的CSV文件,以及高度结构化的Parquet文件。结构化API指以下三种核心分布集合类型的API:
3. Dataset类型
4. DataFrame类型
5. SQL表和视图
结构化API适用于批处理和流处理。结构化API是在编写大部分数据处理程序时会用到的基础抽象。
Spark中的DataFrame和Dataset代表不可变的数据集合,可以通过它指定对特定位置数据的操作,该操作将以惰性评估方式执行。
1.1 Schema
Schema定义了DataFrame的列名和类型,可以手动定义或者从数据源读取模式。
1.2 结构化API执行概述
- 编写DataFrame/Dataset/SQL代码
- 如果代码能有效执行,Spark将其转换为一个逻辑执行计划(Logical Plan)。
- Spark将此逻辑执行计划转化为一个物理执行计划(Physical Plan),检查可行的优化策略,并在此过程中检查优化。
- spark在集群上执行该物理执行计划(RDD操作)
编写的代码通过控制台提交给Spark,或者以一个Spark作业的形式提交。然后代码将交由Catalyst优化器决定如何操作,并制定一个执行计划。最后代码被运行,得到的结果返回给用户。
逻辑计划执行的第一阶段旨在获取用户代码并将其转换为逻辑计划。
逻辑计划仅代表一组抽象转换,并不涉及执行器或驱动器,他只是将用户的表达式集合转换为最优的版本。物理计划
在成功创建优化的逻辑计划后,Spark开始执行物理计划流程。物理计划(通常称为Spark计划)通过生成不同的物理执行操作,并通过代价模型进行比较分析,从而确定如何在集群上执行逻辑计划。
执行
在选择一个物理计划时,Spark将所有代码运行在Spark的底层编程接口RDD上。Spark在运行时执行进一步优化,生成可以在执行期间优化任务或阶段的本地Java字节码,最终将结果返回给用户。
2. 结构化API的基本操作
DataFrame的操作:聚合操作(aggregation),窗口函数(window function),连接操作(join)。
DataFrame由记录(record)组成,record是Row类型(与数据库表中的行相似)。一条record由多列组成。
模式定义了DataFrame列的名以及列的数据类型。DataFrame的分区定义了DataFrame以及Dataset在集群上的物理分布,而划分模式定义了partition的分配方式。
2.1 模式
模式定义了DataFrame列的名以及列的数据类型:
- 由数据源来定义模式(读时模式,schema-on-read)
- 自己显示定义
2.2 列和表达式
//引用列
df.col("columnName")
//列作为表达式
expr("someCol") == col("someCol")
expr("someCol"-5) == col("someCol")-5 == expr("someCol") - 5
//访问DataFrame的列
df.columns
表达式是一个DataFrame的列的函数,列就是表达式。
2.3 DataFrame转换操作
在处理DataFrame对象时,通常有以下几个目标:
- 添加行或列
- 删除行或列
- 将一行转换操作为一列(或反之)
- 根据列中的值更改行的顺序
三个工具:(借助这三个工具,可以解决处理DataFrame时遇到的大多数问题)
- 处理列或表达式时的select方法
- 处理字符串表达式时的selectExpr方法
- 有一些操作不针对列,此时org.apache.spark.sql.functions包中包含一组函数方法来提供额外支持。
df.select("col1", "col2").show() //选取两列,并查看
df.select(expr("colName AS recolName")).show() //选取列并改名
df.select(expr("colName AS recolName")).alias("colName") //列名又改为初始的名字
//select后跟expr操作非常常见,此操作可用selectExpr代替
df.selectExpr("colName AS recolName", "colName") //selectExpr创建复杂表达式构建DataFrame
//添加列
df.withColumn("name", lit(1)).show() //添加一列全为1的列
df.withColumn("withCountry", expr("col1 == col2"))//添加一个布尔表达式
//重命名列
df.withColumnRenamed("name", "re_name")
//删除列
df.drop()
//更改列类型(强制类型转换)
df.withColumn("count2", col("count").cast("long"))
//过滤行
df.filter(col("count") < 2)
df.filter("count < 2")
//获得去重后的行,distinct方法
df.select("col1", "col2").distinct().show()
//随机抽样
df.sample(withReplacement, fraction, seed).count()
//随机分割
df.randomSplit(Array(0.25,0.75))
//联合操作,必须保证两个DataFrame具有相同的模式和列数
df.union(newDF)
//行排序
df.sort("count")
//limit方法取记录
df.limit(5) //取前5条记录,行
//重划分和合并
df.rdd.getNumPartitions
df.repartition()
3.处理不同的数据类型
3.1 转换成spark类型:lit()
df.select(lit(5), lit(“five”), lit(5.0))
3.2 处理日期和时间戳类型
程序语言和数据库领域中,处理日期和时间一直都是难题。需要已知跟踪时区,和确保其格式正确可用。spark通过date和timestamp来处理相关问题。
date:日历日期
timestamp:日期和时间信息
//将某一列字符串类型转换为时间戳类型
val dateFrame = "dd/MM/yyyy HH:mm:ss"
val timeStructruedLogon = logon.withColumn("date",to_timestamp(col("date"),dateFrame))
timeStructruedLogon.printSchema()