Spark(Python)学习(五)

Spark SQL

Hive and Shark

Hive是专门基于Hadoop平台的数据仓库产品,在Hadoop平台上提供了SQL查询的能力。
Hive帮助用户在Hadoop上构建一个数据仓库,使用SQL语句查询时,把SQL语句转化为底层MapReduce程序来对底层HDFS中的数据进行查询分析。
Hive本身不存储数据,借助底层HDFS保存数据,Hive本质是一个编程接口。
Spark(Python)学习(五)_第1张图片
Spark(Python)学习(五)_第2张图片
Shark即是Hive on Spark,Shark全盘照搬Hive的框架,Shark把SQL语句转化为底层的Spark程序。相比Hive,Shark的性能提高了10-100倍。
由于Shark完全照搬Hive的框架,导致Shark有两个问题,一是执行计划的优化完全依赖Hive,不方便添加新的优化策略。二是Spark是线程级并行,而MapReduce是进程级并行,Spark为了兼容Hive,在实现上存在线程安全问题,导致Shark不得不使用另外一套独立维护的打了补丁的Hive源码分支。
2014年Spark停止对Shark的开发,转向了Spark SQL。因此在Spark生态系统存在Spark SQL(不再受限于Hive,只是兼容Hive,让使用Hive的用户可以平滑过渡)和Hive on Spark(是一个Hive的发展计划,该计划将Spark作为Hive的底层引擎之一,即Hive将不在受限于一个引擎,可以采用Map-Reduce、Tez、Spark等引擎)
Spark(Python)学习(五)_第3张图片
Spark(Python)学习(五)_第4张图片
【为什要专门设计Spark SQL组件】
关系数据库在大数据时代不能满足要求。首先,用户需要从不同数据源执行各种操作,包括结构化和非结构化数据。其次,用户需要执行高级分析,比如机器学习和图像处理。在实际大数据应用中,经常需要融合关系查询和复杂分析算法。由于市场缺少这样的产品,Spark SQL的出现填补了这个鸿沟。
首先,可以提供DataFrame API,可以对内部和外部各种数据源执行各种关系操作。其次,可以支持大量的数据源和数据分析算法,Spark SQL可以融合:传统关系数据库的结构化数据管理能力和机器学习算法的数据处理能力。

DataFrame

【DataFrame与RDD的区别】

  1. DataFrame使Spark具备了处理大规模结构化数据的能力,不仅比RDD转化方式更加简单易用,而且获得了更高的计算性能。
  2. Spark能够轻松实现从MySQL到DataFrame的转化,并且支持SQL查询。
  3. RDD是分布式的Java对象集合,但是,对象内部结构对于RDD而言却是不可知的。DataFrame是一种以RDD为基础的分布式数据集,提供了详细的结构信息。
  4. RDD看不见内部详细信息,如下图只能能够看见外部的Person,只有当对象获取后,才能获得对象内部详细信息。不同于RDD,DataFrame提供了Person的详细结构信息Name、Age、Height。
    Spark(Python)学习(五)_第5张图片

谈谈RDD、DataFrame、Dataset的区别和各自的优势
Spark DataFrame提供了registerTempTable接口,可将数据对象注册成临时表,便于后续的各种查询操作等。临时表的生命周期和创建该DataFrame的SQLContext有关系,当SQLContext生命周期结束时,该临时表的生命周期也就结束了。

DataFrame的创建

SparkSession是SparkSQL的指挥官(类似于RDD编程中SparkContext对象),SparkSession接口实现对数据加载、转换、处理等功能。SparkSession实现了SQLContext及HiveContext所有功能。
SparkSession支持从不同的数据源加载数据,并把数据转换成DataFrame,并且支持吧DataFrame转换成SQLContext自身中的表,然后使用SQL语句来操作数据。

  • 创建SparkSession对象
from pyspark import SparkContext,SparkConf
from pyspark.sql import SparkSession
spark = SparkSession.builder.config(conf = SparkConf()).getOrCreate()

在启动进入pyspark交互式环境下,pyspark就默认提供了一个SparkContext对象(名称为sc)和一个SparkSession对象(名称为spark),不用手动生成,可以直接用。
读取不同类型数据创建DataFrame
(1)spark.read.text(“people.txt”)
读取文本文件people.txt创建DataFrame
(2)spark.read.json(“people.json”)
读取people.json文件创建DataFrame
(3)spark.read.parquet(“people.parquet”)
读取people.parquet文件创建DataFrame
(4)spark.read.format(“text”).load(“people.txt”)
读取文本文件people.txt
(5)spark.read.format(“json”).load(“people.json”)
读取JSON文件people.json
(6)spark.read.format(“parquet”).load(“people.parquet”)
读取Parquet文件people.parquet
(实际应用中要使用文件的全称路径,本地文件是“file://<文件路径全称>”,分布式文件系统中“hdfs://localhost:9000/文件路径全称>”)

实例:下图为实例文件内容
Spark(Python)学习(五)_第6张图片
在pyspark交互式执行环境下,读取people.json文件
Spark(Python)学习(五)_第7张图片

  • DataFrame的保存
    使用spark.write操作保存DataFrame
df.write.txt("people.txt")
df.write.json("people.json")
df.write.parquet("people.parquet")

df.write.format("text").save("people.txt")
df.write.format("json").save("people.json")
df.write.format("parquet").save("people.parquet")

实例:从文件people.json中创建DataFrame,保存到另外一个JSON文件中
Spark(Python)学习(五)_第8张图片
保存会生成一个名称为newpeople.json的目录(不是文件)和一个名称为newpeople.txt的目录(不是文件)。加载文件内数据只需将目录名称放入读取括号即可。

DataFrame常用操作

>>> df = spark.read.json("people.json")

(1)printSchema():打印模式信息

>>> df.printSchema()
root
 |-- age: long (nullable = true)
 |-- name: string (nullable = true)

(2)select():显示列信息

>>> df.select(df["name"],df["age"]+1).show()
+-------+---------+
|   name|(age + 1)|
+-------+---------+
|Michael|     null|
|   Andy|       31|
| Justin|       20|
+-------+---------+

(3)filter():过滤操作

>>> df.filter(df["age"]>20).show()
+---+----+
|age|name|
+---+----+
| 30|Andy|
+---+----+

(4)groupBy():分组统计

>>> df.groupBy("age").count().show()
+----+-----+
| age|count|
+----+-----+
|  19|    1|
|null|    1|
|  30|    1|
+----+-----+

(5)sort():排序

>>> df.sort(df["age"].desc()).show() # 根据年龄降序排序
+----+-------+
| age|   name|
+----+-------+
|  30|   Andy|
|  19| Justin|
|null|Michael|
+----+-------+

>>> df.sort(df["age"].desc(),df["name"].asc()).show() # 根据年龄降序排序,根据姓名升序排序
+----+-------+
| age|   name|
+----+-------+
|  30|   Andy|
|  19| Justin|
|null|Michael|
+----+-------+

RDD转换得到DataFrame

有利用反射机制推断RDD模式和使用编程方式定义RDD模式两种方法。
(1)利用反射机制推断RDD模式
示例文件内容:
Spark(Python)学习(五)_第9张图片
目标:将people.txt加载到内存,生成DataFrame。
Spark(Python)学习(五)_第10张图片
import Row 是由于之后需要使用Row对象对数据进行一行一行的封装。
调用sparkContext方法,返回一个sparkContext对象。
第一次“.map”操作将数据拆分为字符串列表(例子中是三个元素,每个元素都是一个列表)(map操作不改变元素个数)。
第二次“.map”操作将列表元素分别转化为Row对象。
Spark(Python)学习(五)_第11张图片
spark.createDataFrame(“people”),将people这个RDD转换得到DataFrame。
由于不能直接对DataFrame进行查询,必须将其注册成为临时表才能用于后面的SQL查询。
schemaPeople.createOrReplaceTempView(“people”),这里的引号中的people是指将临时表命名为people,并非最先加载RDD的people。
之后可以针对这个临时表进行SQL查询。
使用spark.sql对临时表进行查询,得到的结果是被封装在DataFrame中。
想要输出,则先使用“.rdd”将DataFrame转换为RDD,再使用“.map”方法将数据转换成需要的模式。

(2)使用编程方式定义RDD模式
此方式适用于无法提前获知数据结构时的情况。
目标:同样将people.txt加载进内存生成DataFrame,完成SQL查询。
通过编程方式实现需要三步:
Spark(Python)学习(五)_第12张图片
Spark(Python)学习(五)_第13张图片
用schemaString定义表头有哪几个字段,用字符串封装。
fields是一个列表,利用for循环将字符串schemaString以空格分隔,生成每个元素都是一个StructField对象的列表(structField源码结构),每个StructField对象用于对应描述每个分隔出来的字段,例如“name”字段会生成一个StructField对象来描述“name”字段,描述字段名称、类型、是否为空。“age”字段同理。由此生成一个列表,其中包含两个StructField对象,每个对象用于描述一个字段。
schema=StructType(fields),将fields对象作为参数,传递给StructType,生成新对象schema用于描述数据库模式。
Spark(Python)学习(五)_第14张图片
strip()方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。(注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。)
spark.createDataFrame(people,schema),将表头与表拼接。
拼接完成后需要将DataFrame注册为临时表才能进行之后的查询。
Spark(Python)学习(五)_第15张图片
results是一个DataFrame对象,只有DataFrame对象可以调用“.show()”方法,RDD不能调用。

Spark SQL读写数据库

Ubuntu安装MySQL

(1)在Linux中启动MySQL数据库

$ service mysql start
$ mysql -u root -p
#屏幕提示输入密码,是mysql的root用户密码,不是linux中root用户密码

(2)创建数据库和表

mysql> create database spark;
mysql> use spark;
mysql> create table student(id int(4),name char(20),gender char(4),age int(4));
mysql> insert into student values(1,'Xueqian','F',23);
mysql> insert into student value(2,'Weiliang','M',24);
mysql> select * from student;

(3)使用Spark SQL来读取MySQL数据库,需要安装JDBC驱动程序
Spark(Python)学习(五)_第16张图片
将下载好的JDBC驱动程序放入spark安装目录下的jars子目录,完成后可以启动pyspark。
Spark(Python)学习(五)_第17张图片
(4)通过JDBC连接MySQL数据库
Spark(Python)学习(五)_第18张图片
“.option”用于增加连接参数
(5)查看数据库内容

mysql> use spark;
Database changed

mysql> select * from student;
+------+----------+--------+------+
| id   | name     | gender | age  |
+------+----------+--------+------+
|    1 | Xueqian  | F      |   23 |
|    2 | Weiliang | M      |   24 |
+------+----------+--------+------+
2 rows in set (0.00 sec)

(6)向spark.student表中插入两条新数据
Spark(Python)学习(五)_第19张图片
插入记录同理与上节创建表,第一步先设置表头,第二步再得到表中记录,第三步将表头和表中记录拼装起来组成注册表,第四步讲表插入到底层关系数据库。
schema = StructType([…]),表头schema是StructType对象,为StructType对象提供初始化参数列表类型的对象,列表中有4个StructField对象,每个StructField对象用于对应描述一个字段。(True代表可以为空)
Spark(Python)学习(五)_第20张图片
由于要生成两条学生信息,所以要封装两个row对象。
studentRDD = spark.sparkContext…,通过“.parallelize”方法得到RDD有两个字符串类型元素,再通过“.map”方法得到由两个列表类型的元素组成的RDD,每个列表包含4个元素。
rowRDD = …,对studentRDD进行转换,转换后得到两个row类型对象,p指向RDD中的列表,第一个row对象中p[0]是3,p[1]是“Rongcheng”,p[2]是“M”,p[3]是26。(数据去头尾部空格,数字化整型)
Spark(Python)学习(五)_第21张图片
studentDF = …,用拼接的方式创造一个DataFrame。
之后将创建的DataFrame保存到关系数据库中。
查看效果:
Spark(Python)学习(五)_第22张图片

你可能感兴趣的:(Spark)