Spark SQL入门、基础语法、API

Spark SQL

概述

Spark为结构化数据处理引入了一个称为Spark SQL的编程模块。它提供了一个称为
DataFrame(数据框)的编程抽象,DF的底层仍然是RDD,并且可以充当分布式SQL查询引 擎。

SparkSQL的由来

SparkSQL的前身是Shark。在Hadoop发展过程中,为了给熟悉RDBMS但又不理解MapReduce的技术人员提供快速上手的工具,Hive应运而生,是当时唯一运行在hadoop上 的SQL-on-Hadoop工具。但是,MapReduce计算过程中大量的中间磁盘落地过程消耗了大 量的I/O,运行效率较低。
后来,为了提高SQL-on-Hadoop的效率,大量的SQL-on-Hadoop工具开始产生,其中表现 较为突出的是:
1)MapR 的 Drill 2)Cloudera的Impala 3)Shark
其中Shark是伯克利实验室Spark生态环境的组件之一,它基于Hive实施了一些改进,比如引 入缓存管理,改进和优化执行器等,并使之能运行在Spark引擎上,从而使得SQL查询的速度 得到10-100倍的提升。

在这里插入图片描述
但是,随着Spark的发展,对于野心勃勃的Spark团队来说,Shark对于hive的太多依赖(如采用hive的语法解析器、查询优化器等等),制约了Spark的One Stack rule them all的既定方针,制约了spark各个组件的相互集成,所以提出了sparkSQL项目。

在这里插入图片描述

SparkSQL抛弃原有Shark的代码,汲取了Shark的一些优点,如内存列存储(In-Memory Columnar Storage)、Hive兼容性等,重新开发了SparkSQL代码。

由于摆脱了对hive的依赖性,SparkSQL无论在数据兼容、性能优化、组件扩展方面都得到了 极大的方便。

2014年6月1日,Shark项目和SparkSQL项目的主持人Reynold Xin宣布:停止对Shark的开发,团队将所有资源放SparkSQL项目上,至此,Shark的发展画上了句话。

SparkSql特点

1)引入了新的RDD类型SchemaRDD,可以像传统数据库定义表一样来定义SchemaRDD
2)在应用程序中可以混合使用不同来源的数据,如可以将来自HiveQL的数据和来自SQL的数 据进行Join操作。
3)内嵌了查询优化框架,在把SQL解析成逻辑执行计划之后,最后变成RDD的计算

为什么sparkSQL的性能会得到怎么大的提升呢? 主要sparkSQL在下面几点做了优化:
1)内存列存储(In-Memory Columnar Storage)

列存储的优势:

①海量数据查询时,不存在冗余列问题。如果是基于行存储,查询时会产生冗余列,消除冗余 列一般在内存中进行的。或者基于行存储的查询,实现物化索引(建立B-tree B+tree),但是物化索引也是需要耗费cpu的

②基于列存储,每一列数据类型都是同质的,好处一可以避免数据在内存中类型的频繁转换。 好处二可以采用更高效的压缩算法,比如增量压缩算法,二进制压缩算法。性别:男 女 男女 0101

SparkSQL的表数据在内存中存储不是采用原生态的JVM对象存储方式,而是采用内存列存 储,如下图所示。
在这里插入图片描述

该存储方式无论在空间占用量和读取吞吐率上都占有很大优势。

对于原生态的JVM对象存储方式,每个对象通常要增加12-16字节的额外开销
(toString、hashcode等方法),如对于一个270MB的电商的商品表数据,使用这种方式读 入内存,要使用970MB左右的内存空间(通常是2~5倍于原生数据空间)。

另外,使用这种方式,每个数据记录产生一个JVM对象,如果是大小为200GB的数据记录, 堆栈将产生1.6亿个对象,这么多的对象,对于GC来说,可能要消耗几分钟的时间来处理
(JVM的垃圾收集时间与堆栈中的对象数量呈线性相关。显然这种内存存储方式对于基于内 存计算的spark来说,很昂贵也负担不起)

SparkSql的存储方式:对于内存列存储来说,将所有原生数据类型的列采用原生数组来存
储,将Hive支持的复杂数据类型(如array、map等)先序化后并接成一个字节数组来存储。

此外,基于列存储,每列数据都是同质的,所以可以降低数据类型转换的CPU消耗。此外, 可以采用高效的压缩算法来压缩,是的数据更少。比如针对二元数据列,可以用字节编码压缩

来实现(010101)

这样,每个列创建一个JVM对象,从而可以快速的GC和紧凑的数据存储;额外的,还可以使用低廉CPU开销的高效压缩方法(如字典编码、行长度编码等压缩方法)降低内存开销;更有趣的是,对于分析查询中频繁使用的聚合特定列,性能会得到很大的提高,原因就是这些列 的数据放在一起,更容易读入内存进行计算。

SparkSQL入门

概述

SparkSql将RDD封装成一个DataFrame对象,这个对象类似于关系型数据库中的表。创建DataFrame对象
DataFrame就相当于数据库的一张表。它是个只读的表,不能在运算过程再往里加元素。
RDD.toDF(“列名”)

scala> val rdd = sc.parallelize(List(1,2,3,4,5,6))
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at
:21

scala> rdd.toDF(“id”)
res0: org.apache.spark.sql.DataFrame = [id: int]

scala> res0.show#默认只显示20条数据
±–+
| id|
±–+
| 1|
| 2|
| 3|
| 4|
| 5|
| 6|
±–+
scala> res0.printSchema #查看列的类型等属性root
|-- id: integer (nullable = true) 创建多列DataFrame对象
DataFrame就相当于数据库的一张表。
scala> sc.parallelize(List( (1,“beijing”),(2,“shanghai”) ) )

res3: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[5] at parallelize at
:22

scala> res3.toDF(“id”,“name”)
res4: org.apache.spark.sql.DataFrame = [id: int, name: string]

scala> res4.show
±–±-------+
| id| name|
±–±-------+
| 1| beijing|
| 2|shanghai|
±–±-------+

例如3列的
scala> sc.parallelize(List( (1,“beijing”,100780),(2,“shanghai”,560090),(3,“xi’an”,600329))) res6: org.apache.spark.rdd.RDD[(Int, String, Int)] = ParallelCollectionRDD[10] at parallelize at :22

scala> res6.toDF(“id”,“name”,“postcode”)
res7: org.apache.spark.sql.DataFrame = [id: int, name: string, postcode: int]

scala> res7.show
±–±-------±-------+
| id| name|postcode|
±–±-------±-------+
| 1| beijing| 100780|
| 2|shanghai| 560090|
| 3| xi’an| 600329|
±–±-------±-------+
可以看出,需要构建几列,tuple就有几个内容。由外部文件构造DataFrame对象
1)txt文件
txt文件不能直接转换成,先利用RDD转换为tuple。然后toDF()转换为DataFrame。
scala> val rdd = sc.textFile("/root/words.txt")

.map( x => (x,1) )
.reduceByKey( (x,y) => x+y )
rdd: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[18] at reduceByKey at
:21

scala> rdd.toDF(“word”,“count”)
res9: org.apache.spark.sql.DataFrame = [word: string, count: int]

scala> res9.show
±-----±----+
| word|count|
±-----±----+
| spark| 3|
| hive| 1|
|hadoop| 2|
| big| 2|
| scla| 1|
| data| 1|
±-----±----+

2)json文件文件代码:
{“id”:1, “name”:“leo”, “age”:18}
{“id”:2, “name”:“jack”, “age”:19}
{“id”:3, “name”:“marry”, “age”:17}

代码:
import org.apache.spark.sql.SQLContext scala>val sqc=new SQLContext(sc)
scala> val tb4=sqc.read.json("/home/software/people.json") scala> tb4.show

在这里插入图片描述

3)jdbc读取
实现步骤:
1)将mysql 的驱动jar上传到spark的jars目录下
2)重启spark服务
3)进入spark客户端
4)执行代码,比如在Mysql数据库下,有一个test库,在test库下有一张表为tabx 执行代码:
import org.apache.spark.sql.SQLContext scala> val sqc = new SQLContext(sc); scala> val prop = new java.util.Properties scala> prop.put(“user”,“root”)
scala> prop.put(“password”,“root”)
scala>val tabx=sqc.read.jdbc(“jdbc:mysql://hadoop01:3306/weblog”,“tongji”,prop) scala> tabx.show
±–±—+
| id|name|
±–±—+
| 1| aaa|
| 2| bbb|
| 3| ccc|
| 1| ddd|
| 2| eee|
| 3| fff|
±–±—+
注:如果报权限不足,则进入mysql,执行:
grant all privileges on . to ‘root’@‘hadoop01’ identified by ‘root’ with grant option; 然后执行:
flush privileges;

SparkSql基础语法

通过方法来使用

(1)查询df.select(“id”,“name”).show();
(2)带条件的查询

df.select( " i d " , "id", "id",“name”).where($“name” === “bbb”).show()

(3)排序查询

orderBy/sort( " 列 名 " ) 升 序 排 列 o r d e r B y / s o r t ( "列名") 升序排列orderBy/sort( "")orderBy/sort(“列名”.desc) 降序排列orderBy/sort($“列1” , $“列2”.desc) 按两列排序

df.select( " i d " , "id", "id",“name”).orderBy($“name”.desc).show

df.select( " i d " , "id", "id",“name”).sort($“name”.desc).show

tabx.select( " i d " , "id", "id",“name”).sort( " i d " , "id", "id",“name”.desc).show

(4)分组查询

groupBy(“列名”, …).max(列名) 求最大值
groupBy(“列名”, …).min(列名) 求最小值
groupBy(“列名”, …).avg(列名) 求平均值
groupBy(“列名”, …).sum(列名) 求和
groupBy(“列名”, …).count() 求个数
groupBy(“列名”, …).agg 可以将多个方法进行聚合

scala>val rdd = sc.makeRDD(List((1,“a”,“bj”,100),(2,“b”,“sh”,80),(3,“c”,“gz”,50),(4,“d”,“bj”,45))); scala>val df = rdd.toDF(“id”,“name”,“addr”,“score”);
scala>df.groupBy(“addr”).count().show()
在这里插入图片描述
scala>df.groupBy(“addr”).agg(max( " s c o r e " ) , m i n ( "score"), min( "score"),min(“score”), count($"*")).show
在这里插入图片描述

(5)连接查询

scala>val dept=sc.parallelize(List((100,“caiwubu”),(200,“yanfabu”))).toDF(“deptid”,“deptname”) scala>val emp=sc.parallelize(List((1,100,“zhang”),(2,200,“li”),(3,300,“wang”))).toDF(“id”,“did”,“name”) scala>dept.join(emp,$“deptid” === " d i d " ) . s h o w s c a l a > d e p t . j o i n ( e m p , "did").show scala>dept.join(emp, "did").showscala>dept.join(emp,“deptid” === $“did”,“left”).show

把小表写在前面(左边),底层自动优化,实现Map side Join
左向外联接的结果集包括 LEFT OUTER子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
scala>dept.join(emp,$“deptid” === $“did”,“right”).show

(6)执行运算

val df = sc.makeRDD(List(1,2,3,4,5)).toDF(“num”); df.select($“num” * 100).show

(7)使用列表

val df = sc.makeRDD(List((“zhang”,Array(“bj”,“sh”)),(“li”,Array(“sz”,“gz”)))).toDF(“name”,“addrs”) df.selectExpr(“name”,“addrs[0]”).show

(8)使用结构体

{“name”:“陈晨”,“address”:{“city”:“西安”,“street”:“南二环甲字1号”}}
{“name”:“娜娜”,“address”:{“city”:“西安”,“street”:“南二环甲字2号”}}

val df = sqlContext.read.json(“file:///root/work/users.json”)

dfs.select(“name”,“address.street”).show

(9)其他

df.count//获取记录总数
val row = df.first()//获取第一条记录

val take=df.take(2) //获取前n条记录
val value = row.getString(1)//获取该行指定列的值
df.collect //获取当前df对象中的所有数据为一个Array 其实就是调用了df对象对应的底层的rdd的collect方法

通过sql语句来调用

(0)创建表df.registerTempTable(“tabName”) (1)查询

val sqc = new org.apache.spark.sql.SQLContext(sc); val df =
sc.makeRDD(List((1,“a”,“bj”),(2,“b”,“sh”),(3,“c”,“gz”),(4,“d”,“bj”),(5,“e”,“gz”))).toDF(“id”,“name”,“addr”); df.registerTempTable(“stu”);
sqc.sql(“select * from stu”).show()

(2)带条件的查询val df =

sc.makeRDD(List((1,“a”,“bj”),(2,“b”,“sh”),(3,“c”,“gz”),(4,“d”,“bj”),(5,“e”,“gz”))).toDF(“id”,“name”,“addr”); df.registerTempTable(“stu”);
sqc.sql(“select * from stu where addr = ‘bj’”).show()

(3)排序查询

val sqlContext = new org.apache.spark.sql.SQLContext(sc); val df =
sc.makeRDD(List((1,“a”,“bj”),(2,“b”,“sh”),(3,“c”,“gz”),(4,“d”,“bj”),(5,“e”,“gz”))).toDF(“id”,“name”,“addr”); df.registerTempTable(“stu”);
sqlContext.sql(“select * from stu order by addr”).show()

sqlContext.sql(“select * from stu order by addr desc”).show()

(4)分组查询

val sqlContext = new org.apache.spark.sql.SQLContext(sc); val df =
sc.makeRDD(List((1,“a”,“bj”),(2,“b”,“sh”),(3,“c”,“gz”),(4,“d”,“bj”),(5,“e”,“gz”))).toDF(“id”,“name”,“addr”); df.registerTempTable(“stu”);
sqlContext.sql(“select addr,count(*) from stu group by addr”).show()

(5)连接查询

val sqlContext = new org.apache.spark.sql.SQLContext(sc);

val dept=sc.parallelize(List((100,“财务部”),(200,“研发部”))).toDF(“deptid”,“deptname”)
val emp=sc.parallelize(List((1,100,“张财务”),(2,100,“李会计”),(3,300,“王艳发”))).toDF(“id”,“did”,“name”) dept.registerTempTable(“deptTab”);
emp.registerTempTable(“empTab”);

sqlContext.sql(“select deptname,name from dept inner join emp on dept.deptid = emp.did”).show()

(6)执行运算

val sqlContext = new org.apache.spark.sql.SQLContext(sc); val df = sc.makeRDD(List(1,2,3,4,5)).toDF(“num”); df.registerTempTable(“tabx”)
sqlContext.sql(“select num * 100 from tabx”).show();

(7)分页查询

val sqlContext = new org.apache.spark.sql.SQLContext(sc);

val df = sc.makeRDD(List(1,2,3,4,5)).toDF(“num”); df.registerTempTable(“tabx”) sqlContext.sql(“select * from tabx limit 3”).show();

(8)查看表

sqlContext.sql(“show tables”).show

(9)类似hive方式的操作

scala>val hiveContext = new org.apache.spark.sql.hive.HiveContext(sc) scala>hiveContext.sql(“create table if not exists zzz (key int, value string) row format delimited fields terminated by ‘|’”)
scala>hiveContext.sql("load data local inpath ‘file:///home/software/hdata.txt’ into

table zzz")

scala>hiveContext.sql(“select key,value from zzz”).show

(10)案例

val sqlContext = new org.apache.spark.sql.SQLContext(sc);

val df = sc.textFile(“file:///root/work/words.txt”).flatMap{ _.split(" ") }.toDF(“word”) df.registerTempTable(“wordTab”)
sqlContext.sql(“select word,count(*) from wordTab group by word”).show

SparkSql API

通过api使用sparksql 实现步骤:
1)打开scala IDE开发环境,创建一个scala工程
2)导入spark相关依赖jar包
在这里插入图片描述
3)创建包路径以object类4)写代码
代码示意:

import org.apache.spark.SparkConf import org.apache.spark.SparkContext import org.apache.spark.sql.SQLContext
object Demo01 {
def main(args: Array[String]): Unit = {
val conf=new SparkConf().setMaster("spark://hadoop01:7077").setAppName("sqlDemo01");
val sc=new SparkContext(conf)
val sqlContext=new SQLContext(sc)
val rdd=sc.makeRDD(List((1,"zhang"),(2,"li"),(3,"wang")))

import sqlContext.implicits._ val df=rdd.toDF("id","name") df.registerTempTable("tabx")
val df2=sqlContext.sql("select * from tabx order by name"); val rdd2=df2.toJavaRDD;
//将结果输出到linux的本地目录下,当然,也可以输出到HDFS上
rdd2.saveAsTextFile("file:///home/software/result");
}
}

5)打jar包,并上传到linux虚拟机上6)在spark的bin目录下
执行:sh spark-submit --class cn.tedu.sparksql.Demo01 ./sqlDemo01.jar 7)最后检验

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值