文章目录
SparkSQL简介
Shark
同样的,在Spark上也有将SQL语句转化成spark程序去执行
但是刚刚开始是叫做Shark
Shark即Hive on Spark,为了实现与Hive兼容,Shark在HiveQL方面重用了Hive中Hive的解析,逻辑执行计划翻译,执行计划优化等逻辑,可以近似认为仅将物理执行计划从MapReduce作业替换为Spark作业,通过Hive的HiveQL解析,把HiveQL翻译成Spark上的RDD操作
Shark基本是全盘照搬Hive框架,只是对底层的翻译模块进行修改(Hive把SQL转成MapReduce程序,Shark把SQL转成Spark程序)
Shark的两个问题
- 因为Shark是照搬Hive的所以导致执行计划的优化策略依赖Hive,不方便添加优化策略(Hive是对MapReduce的);
- MapReduce是进程级并行,Spark是线程级并行,因此,Spark在兼容Hive的实现上存在线程安全问题,导致Shark不得不使用另外一套独立维护的打了补丁的Hive源码分支
SparkSQL
SparkSQL的设计
SparkSQL出现的原因
- 关系型数据库已经很流行
- 关系型数据库在大数据时代已经不能满足要求
首先, 用户需要从不同数据源执行各种操作, 包括结构化, 半结构化和非结构化数据
其次, 用户需要执行高级分析, 比如机器学习和图像处理- 在实际大数据应用中, 经常需要融合关系查询和复杂分析算法, 但是缺少这样的系统
于是SparkSQL的出现填补了这个鸿沟:
- SparkSQL 提供DataFrame API, 可以对内部和外部各种数据源执行各种关系型操作
- 可以支持大数据中的大量数据源和数据分析算法; SparkSQL可以融合传统关系数据库的结构化数据管理能力和机器学习算法的数据处理能力
DataFrame
我们知道Spark Core的数据抽象是RDD
而SparkSQL的数据抽象是DataFrame
DataFrame:使得Spark具备了处理大规模结构化数据的能力, 不仅比原有的RDD转化方式更加简单易用, 而且获得了更高的计算性能
SparkSQL能够轻松实现从MySQL到DataFrame的转化, 并且支持SQL查询
RDD是分布式的Java对象的集合, 但是, 对象内部结构对于RDD是不可知的
DataFrame是一种以RDD为基础的分布式数据集, 提供了详细的结构信息
从Spark2.0以上版本开始, Spark使用全新的SparkSession接口代替了Spark1.6中的SQLContext及HiveContext接口来实现其对数据加载, 转换, 处理等功能
SparkSession实现了SQLContext及HiveContext所有功能
SparkSession支持从不同的数据源加载数据, 并把数据转换成DataFrame, 并且支持把DataFrame转换成SQLContext自身中的表, 然后使用SQL语句来操作数据. SparkSession亦提供了HiveQL以及其他依赖于Hive的功能
DataFrame的创建
读取json格式的时候不用指定表头,show出来的就是一个二维表
读取text格式的话,是没有表头的,得自己定义,可以先用SparkContext读取文件得到RDD然后使用SparkSession对象把RDD转换成DF,并指定表头
SparkSession对象读取文件得到DF格式或者将RDD进行转换为DF(转化用到ROW类)
from pyspark import SparkContext,SparkConf
from pyspark.sql import SparkSession
# 得到一个SparkSession的实例
spark = SparkSession.builder.config(conf=SparkConf()).getOrCreate()
# 读取文本文件创建DataFrame
# 读取txt文件
spark.read.text("file:///usr/local/spark/examples/src/main/resources/people.txt")
spark.read.format("text").load("file:///usr/local/spark/examples/src/main/resources/people.txt")
# 读取json文件
spark.read.json("file:///usr/local/spark/examples/src/main/resources/people.json")
spark.read.format("json").load("file:///usr/local/spark/examples/src/main/resources/people.json")
# 读取parquet文件
spark.read.parquet("file:///usr/local/spark/examples/src/main/resources/people.parquet")
spark.read.format("parquet").load("file:///usr/local/spark/examples/src/main/resources/people.parquet")
# 调用.show()方法显示数据
spark.show()
RDD转换得到DataFrame
在创建DF的时候要指定字段,字段可以在设置RDD转换操作的时候用Row来指定
或者在创建DF的时候使用参数来指定字段名
from pyspark import SparkContext,SparkConf
from pyspark.sql import SparkSession
from pyspark.sql import Row
conf = SparkConf().setMaster("local").setAppName("work2")
sc = SparkContext(conf=conf)
spark = SparkSession.builder.config(conf=conf).getOrCreate()
# 读取rdd形式的文件
rddFile = sc.textFile(r"file:///C:\Users\13123\Desktop\sparkCourseFile\employee.txt")
# 对rdd进行转换操作,便于后续变成DF
## 方法一:在rdd转换的时候使用Row类,指定字段,然后创建DF
rdd1 = rddFile.map(lambda lines:lines.split(",")).map(lambda line:Row(id=int(line[0]),name=line[1],age=int(line[2])))
dfFile1 = spark.createDataFrame(rdd1)
print(dfFile1.show())
print(type(dfFile1))
## 方法二:在创建df时指定表头
rdd2 = rddFile.map(lambda lines:lines.split(","))
dfFile2 = spark.createDataFrame(rdd2,schema=["id","name","age"])
print(dfFile2.show())
print(type(dfFile2))
+---+---------+---+
| id| name|age|
+---+---------+---+
| 1| Wuyifan| 30|
| 2| Mike| 29|
| 3| Gouzi| 29|
| 4| Tom| 18|
| 5| Lilei| 16|
| 6| Ella| 36|
| 7| Liming| 19|
| 8|Hanmeimei| 20|
| 9| Maodan| 22|
| 10| Zhangsan| 33|
+---+---------+---+
None
<class 'pyspark.sql.dataframe.DataFrame'>
+---+---------+---+
| id| name|age|
+---+---------+---+
| 1| Wuyifan| 30|
| 2| Mike| 29|
| 3| Gouzi| 29|
| 4| Tom| 18|
| 5| Lilei| 16|
| 6| Ella| 36|
| 7| Liming| 19|
| 8|Hanmeimei| 20|
| 9| Maodan| 22|
| 10| Zhangsan| 33|
+---+---------+---+
None
<class 'pyspark.sql.dataframe.DataFrame'>
1. 利用反射机制推断RDD模式
from pyspark import SparkConf,SparkContext
from pyspark.sql import SparkSession
from pyspark import Row
conf = SparkConf().setMaster("local").setAppName("sparksql")
# 设置配置内容
sc = SparkContext(conf=conf)
# 设置SparkContext的对象,用来操做RDD
spark = SparkSession.builder.config(conf=conf).getOrCreate()
# 构建SparkSQL的对象
peopleText = sc.textFile("file:///usr/local/spark/examples/src/main/resources/people.txt")
# 读取文件,变成rdd
rdd = peopleText.map(lambda x:x.split(",")).map(lambda x:Row(name=x[0],age=x[1])) # 字段名一定要给!!!
# 对rdd进行操作
schemaPeople = spark.createDataFrame(rdd)
# 将rdd转变成DF,或者上面不给.map(),直接在createDataFrame()给schema参数,可以是列表中包含表头
# schemaPeople.createTempView()
schemaPeople.createOrReplaceTempView("myRddToDf") # 设置视图名称
# 创建临时视图,以便于后续的查询使用
personsDF = spark.sql("select name,age from myRddToDf where age>20") # 查询时从视图中查询的!!!
# 查询
personsRDD = personsDF.rdd.map(lambda p:"Name:"+p.name+","+"Age:"+str(p.age))
# 原来是二维表,现在被转换成被封装起来的,包含有两个成员变量的对象
# DataFrame中的每个元素都是一行记录,包括name个age两个,分别用p.name和p.age来获取值
print(personsRDD.foreach(print))
2. 用编程的方式定义RDD模式
from pyspark import Row
from pyspark import SparkConf, SparkContext
from pyspark.sql import SparkSession
from pyspark.sql.types import *
conf = SparkConf().setMaster("local").setAppName("sparksql")
sc = SparkContext(conf=conf)
spark = SparkSession.builder.config(conf=conf).getOrCreate()
# 第一步:制作表头
schemaString = "name age"
fields = [StructField(field_name,StringType(),True) for field_name in schemaString.split(" ")]
schema = StructType(fields)
# 第二步:制作表中的记录
lines = sc.textFile("file:///usr/local/spark/examples/src/main/resources/people.txt")
parts = lines.map(lambda x:x.split(","))
people = parts.map(lambda x:Row(x[0],x[1]))
# 第三步:把表头和表中的记录拼装在一起
schemaSPeople = spark.createDataFrame(people,schema)
schemaSPeople.createTempView("people")
result = spark.sql("select name,age from people")
print(result.show())
DataFrame的保存
# 保存DataFrame文件
# 保存为txt文件
df.write.txt("xxxxxx.txt")
df.write.format("text").save("xxxxxx.txt")
# 保存为json文件
df.write.txt("xxxxxx.json")
df.write.format("json").save("xxxxxx.json")
# 保存为parquet文件
df.write.txt("xxxxxx.parquet")
df.write.format("parquet").save("xxxxxx.parquet")
# 保存的是目录,而不是文件!!!
DataFrame常用操作
只是对df
方法 | 描述 |
---|---|
printSchema() | 打印出DF的模式信息 |
select(…) | |
filter(…) | |
groupBy(…) | |
count() | |
sort(…) | |
在DataFrame中使用SQL语句
使用SQL语句的前提是要创建一个临时表
df.createTempViem(“临时表的名称”)
spark.sql(“select * from 临时表的名称 where”)
连接MySQL
连接Mysql数据库要下载JDBC的驱动程序
然后将驱动安装至spark/jars目录下
读取MySQL内的数据
from pyspark import SparkConf,SparkContext
from pyspark.sql import SparkSession
conf = SparkConf().setMaster("local").setAppName("testJdbc")
sc = SparkContext(conf=conf)
spark = SparkSession.builder.config(conf=conf).getOrCreate()
jdbfDF = spark.read.format("jdbc")\ # 以jdbc方式读取文件
.option("driver","com.mysql.jdbc.Driver")\ # 用.option设置连接参数,连接到mysql
.option("url","jdbc:mysql://localhost:3306/spark")\
.option("dbtable","student")\
.option("user","root")\
.option("password","password")\
.load()
print(jdbfDF.show())
写入数据到Mysql
from pyspark import SparkConf,SparkContext
from pyspark.sql import SparkSession
from pyspark.sql import Row
from pyspark.sql.types import *
conf = SparkConf().setMaster("local").setAppName("testJdbc")
sc = SparkContext(conf=conf)
spark = SparkSession.builder.config(conf=conf).getOrCreate()
# 设置模式信息,(字段,将字段信息封装到列表中)
schema = StructType([
StructField("id",IntegerType(),True), # 字段命为id,字段类型为interger,True表示可以为空
StructField("name",StringType(),True),
StructField("gender",StringType(),True),
StructField("age",IntegerType(),True)
])
# 设置两条数据,表示两个学生的信息
studentRDD = sc.parallelize(["3 Rongcheng2 M 26","4 Guanhua2 M 27"]).map(lambda x:x.split(" "))
# 创建Row对象,每个Row对象都是一行要插入的数据,要插入的内容封装到Row中
rowRDD = studentRDD.map(lambda x:Row(int(x[0].strip()),x[1].strip(),x[2].strip(),int(x[3].strip())))
# rowRDD中有两个元素,每个元素都是一个Row对象,每个Row对象都封装了要插入的数据
# 建立Row对象和模式之间的对应关系得到的是DF
studentDF = spark.createDataFrame(rowRDD,schema)
# 写入数据库
prop={}
prop["user"] = "root"
prop["password"] = "password"
prop["driver"] = "com.mysql.jdbc.Driver"
studentDF.write.jdbc("jdbc:mysql://localhost:3306/spark","student","append",prop)