Spark基础之:Spark SQL介绍

一.Spark SQL的概述

1、Spark SQL来源

Hive是目前大数据领域,事实上的数据仓库标准。

Hive与RDBMS的SQL模型比较类似,容易掌握。 Hive的主要缺陷在于它的底层是基于MapReduce的,执行比较慢。

在Spark 0.x版的时候推出了Shark,Shark与Hive是紧密关联的,Shark底层很多东西还是依赖于Hive,修改了内存管理、物理计划、执行三个模块,底层使用Spark的基于内存的计算模型,性能上比Hive提升了很多倍。

在Spark 1.x的时候Shark被淘汰。在2014 年7月1日的Spark Summit 上, Databricks宣布终止对Shark的开发,将重点放到 Spark SQL 上。

Shark终止以后,产生了两个分支:

  • 1). Hive on Spark
    hive社区的,源码在hive中

  • 2). Spark SQL(Spark on Hive)
    Spark社区,源码在Spark中,支持多种数据源,多种优化技术,扩展性好很多;

Spark SQL的源码在Spark中,而且新增了许多的优化代码,如果追求速度,例如数据分析的时候,可以使用Hive on Spark,如果追求性能,例如生产的定时报表的时候,应该使用Spark SQL。

2、从代码看Spark SQL的特点

我们来对比Spark RDD、Dataframe、SQL代码实现wordcount:

我们可以看到,Spark SQL代码看起来与关系型数据库是一致的,从上图可以看到Spark SQL的特点:

  • 1). 集成
    通过Spark SQL或DataFrame API运行Spark 程序,操作更加简单、快速.

    从上图可以看到,Spark SQL和DataFrame底层其实就是调用RDD

  • 2). 统一的数据访问
    DataFrame 和SQL提供了访问各种数据源的通用方式,包括Hive、Avro、Parquet、ORC、JSON和JDBC。您甚至可以跨这些数据源连接数据。

  • 3). Hive集成
    在现有的数据仓库上运行SQL或HiveQL查询。

  • 4). 标准的连接
    服务器模式为业务智能工具提供行业标准的JDBC和ODBC连接。

3、从代码运行速度看来看Spark SQL


从上图我们可以看到:
1). Python操作RDD比Java/Scala慢一倍以上
2). 无论是那种语言操作DataFrame,性能几乎一致

那么为什么Python用RDD这么慢?
为什么用Python写的RDD比Scala慢一倍以上,两种不同的语言的执行引擎,上下文切换、数据传输。

Spark SQL其实底层调用的也是RDD执行,其实中间的执行计划进行了优化,而且是在Spark的优化引擎里面,所以无论是那种语言操作DataFrame,性能几乎一致

二.Spark SQL数据抽象

Spark SQL提供了两个新的抽象,分别是DataFrame 和Dataset;

Dataset是数据的分布式集合。Dataset是Spark 1.6中添加的一个新接口,它提供了RDDs的优点(强类型、使用强大lambda函数的能力)以及Spark SQL优化的执行引擎的优点。可以从JVM对象构造数据集,然后使用函数转换(map、flatMap、filter等)操作数据集。数据集API可以在Scala和Java中使用。Python不支持Dataset API。但是由于Python的动态特性,Dataset API的许多优点已经可以使用了(例如,您可以通过名称natural row. columnname访问行字段)。R的情况也是类似的。

DataFrame 是组织成命名列的Dataset。它在概念上相当于关系数据库中的表或R/Python中的数据框架,但在底层有更丰富的优化。数据框架可以从各种各样的数据源构建,例如:结构化数据文件、Hive中的表、外部数据库或现有的rdd。DataFrame API可以在Scala、Java、Python和r中使用。在Scala和Java中,DataFrame是由行数据集表示的。在Scala API中,DataFrame只是Dataset[Row]的类型别名。而在Java API中,用户需要使用Dataset来表示DataFrame。

DataFrame

DataFrame的前身是SchemaRDD。Spark1.3更名为DataFrame。不继承RDD,自己实现RDD的大部分功能。与RDD类似,DataFrame也是一个分布式数据集

  1. DataFrame可以看做分布式Row对象的集合,提供了由列组成的详细模式信息,使其可以得到优化,DataFrame不仅有比RDD更多的算子,还可以进行执行计划的优化

  2. DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema

  3. DataFrame也支持嵌套数据类型(struct、array和Map)

  4. DataFrame API提供的是一套高层的关系操作,比函数式RDD API更加优化,门槛低

  5. DataFrame的劣势在于在编译期缺少类型安全检查,导致运行时出错。

1)DataFrame的组成

DataFrame是一个二维表的结构,既然是表结构,就有无法绕开的三点:

  • 表结构描述

基于这个前提,DataFrame的组成如下:

在结构层面:

  • StructType对象描述整个DataFrame的表结构。
  • StructField描述一个列的信息。

在数据层面:

  • row对象记录一行数据。
  • Column对象记录一列数据并包含列的信息。

    代码示例:

    一个StructField记录:列明,列类型,列是否运行为空,多个StructField组成一个StructType对象。
    一个StructType对象可以描述一个DataFrame:有几个列,每个列的名字和类型,每个列是否为空。

2)DataFrame的代码构建

DataFrame对象可以从rdd转换而来,都是分布式的数据集,其实就是转换一下内部的存储结构,转换为二维表的结构。

<1>基于rdd的方式1

调用Spark

# 首先构建一个RDD rdd[(name, age), ()]
rdd = sc.textGile("../data/sql/people.txt").\
	map(lambda x: x.split(',')).\
	map(lambda x: [x[0], int(x[1])])

#构建DF方式1
df = spark.createDataFrame(rdd, schema = ['name', 'age'])

# 打印表结构
df.printSchema()

# 打印20行数据
df.shwow()

df.createTempView("table1") # 给这个表起一个别名
spark.sql("select * from table1 where age< 30").show()

通过SparkSession对象的createDataFrame方法来将rdd转换为DataFrame。
这里只传列名称,类型从RDD中进行推断,是否允许为空默认为允许(Ture)。

<2>基于rdd的方式2

通过StructType对象来定义Dataframe的“表结构”转换rdd

# 创建DF 首先创建RDD 将RDD转换为DF
rdd =  sc.textFile("../data/sql/stu_score.txt").\
	map(lambda x: x.split(',')).\
	map(lambda x: (int(x[0]), x[1], int(x[2])))

# StructTYpe 类
# 这个类可以定义整个DataFrame中的Schema
schema = StructType().\
	add("id", IntegerTYpe(), nullable = False).\
	add("name", StringType(), nullable = True).\
	add("score", IntegerType(), nullable = False)
# 一个add方法定义一个列的信息,如果有3个列,就写3个add
# add方法: 参数1:列名称, 参数2:列类型, 参数3:是否允许为空
df = spark.createDataFrame(rdd, schema)
sd.printSchema()
df.show()
<3>基于rdd的方式3

使用RDD的toDF方法转换RDD

# 创建DF 首先创建RDD 将RDD转换为DF
rdd =  sc.textFile("../data/sql/stu_score.txt").\
	map(lambda x: x.split(',')).\
	map(lambda x: (int(x[0]), x[1], int(x[2])))

# StructTYpe 类
# 这个类可以定义整个DataFrame中的Schema
schema = StructType().\
	add("id", IntegerTYpe(), nullable = False).\
	add("name", StringType(), nullable = True).\
	add("score", IntegerType(), nullable = False)
# 一个add方法定义一个列的信息,如果有3个列,就写3个add
# add方法: 参数1:列名称, 参数2:列类型, 参数3:是否允许为空

# 方式1:只传列名,类型靠推断,是否允许为空是true
df = rdd.toDF(['id', 'subject', 'score'])
df.printSchema()
df.show()

# 方式2:传入完整的Schema描述对象StructType
df = rdd.toDF(schema)
df.printSchema()
df.show()
<4>基于pandas的DataFrame

将pandas的DataFrame对象,转变为分布式的SparkSQL DataFrame对象。

# 构建pandas的DF
pdf = pd.DataFrame({
	"id": [1, 2, 3]
	"name": ["张三", "李四", "王五"]
	"age": [11, 12, 13]
})
# 将Pandas的DF对象转换成Spark的DF
df = spark.createDataFrame(pdf)
df.printSchema()
df.show()

3)DataFrame读取外部数据

DataFrame可以读取的文件格式有:json文件、csv文件、parquet文件等。下面我着重介绍几个常见的文件格式的读取。

<1>DataFrame读取json文件

读取json数据源
使用format(“json”)读取json数据

代码示例:

df = spark.read.format("json").\
	load("../data/sql/people.json")
# JSON类型一般不用写schema,json自带有列名和列的类型
df.printSchema()
df.show()
<2>DataFrame读取csv文件

读取csv数据源
使用format(“csv”)读取json数据

代码示例:

df = spark.read.format("csv").\
	option("sep", ";").\ # 列分隔符
	option("header", False).\ # 是否有csv标头
	option("ending", "utf-8").\ # 指定列名和类型
	load("../data/sql/people.csv") # 路径
df.printSchema()
df.show()
<3>DataFrame读取parquet文件

读取parquet数据源
使用format(“parquet”)读取json数据

代码示例:

# parquet自带schema,直接load就可以
df = spark.read.format("parquet").\
	load("../data/sql/people.parquet")
df.printSchema()
df.show()

4)DataFrame数据写出

SparkSQL统一写出DataFrame数据
统一API语法:

df.write.mode().format().option(K, V).save(PATH)
# mode, 传入模式字符串可选:append 追加, overwrite 覆盖, ignore 忽略,error 重复就报异常(默认的)
# format, 传入格式字符串, 可选: text, csv, json, parquet, orc, avro, jdbc
# 注意:text源只支持单列的df写出
# option设置属性, 如:.option("sep",",")
# save 写出的路径,支持本地文件和HDFS

代码示例:

# write text 写出,只能写出一个单列数据
df.select(F.concat_ws("---", "user_id", "movie_id", "rank", "ts")).\
	write.\
	mode("overwrite").\
	format("text").\
	save("../data/output/sql/text")
	
# write CSV 写出
df.write.mode("overwrite").\
	format("csv").\
	option("sep", ",").\
	option("header", True).\
	save("../data/output/sql/csv")

# write json 写出
df.write.mode("overwrite").\
	format("json").\
	save("../data/output/sql/json")

# write Parquet 写出
df.write.mode("overwrite").\
	format("parquet").\
	save("../data/output/sql/parquet")

# b不给format,默认parquet写出
df.write.mode("overwite").save("../data/output/sql/parquet_default")

三.Spark SQL定义UDF函数



四.Spark SQL执行流程

参考文章链接:https://blog.csdn.net/u010520724/article/details/116267767

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嗷嗷的特Man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值