Spark SQL

Spark SQL是Spark用来处理结构化数据的一个模块,它提供了2个编程抽象:DataFrame和DataSet,并且作为分布式SQL查询引擎的作用。

Hive SQL是转换成MapReduce然后提交到集群上执行,大大简化了编写MapReduc的程序的复杂性,由于MapReduce这种计算模型执行效率比较慢。所有Spark SQL的应运而生,它是将Spark SQL转换成RDD,然后提交到集群执行,执行效率非常快!

 SparkSession

在spark2.0中,引入SparkSession(作为DataSet和DataFrame API的切入点)作为Spark最新的SQL查询起始点,实质上是SQLContext和HiveContext的组合(未来可能还会加上StreamingContext); 为了向后兼容,所以在SQLContext和HiveContext上可用的API在SparkSession上同样是可以使用的。

SparkSession内部封装了sparkContext、SparkConf、SQLContext,所以计算实际上是由sparkContext完成的。

 ---- 为用户提供一个统一的切入点使用Spark 各项功能

 ---- 允许用户通过它调用 DataFrame 和 Dataset 相关 API 来编写程序

 --- 与 Spark 交互之时不需要显示的创建 SparkConf, SparkContext 以及 SQlContext,这些对象已经封闭在 SparkSession 中

DataFrame

在Spark中,DataFrame是一种以RDD为基础的分布式数据集,类似于传统数据库中的二维表格。DataFrame与RDD的主要区别在于,前者带有schema元信息,即DataFrame所表示的二维表数据集的每一列都带有名称和类型。这使得Spark SQL得以洞察更多的结构信息,从而对藏于DataFrame背后的数据源以及作用于DataFrame之上的变换进行了针对性的优化,最终达到大幅提升运行时效率的目标。反观RDD,由于无从得知所存数据元素的具体内部结构,Spark Core只能在stage层面进行简单、通用的流水线优化。

创建

在Spark SQL中SparkSession是创建DataFrame和执行SQL的入口,创建DataFrame有三种方式:

  通过Spark的数据源进行创建;从一个存在的RDD进行转换;还可以从Hive Table进行查询返回。

读取json文件创建DataFrame

  1. spark读取json按行读取;只要一行符合json的格式即可;
    scala> val rdd = spark.read.json("/opt/module/spark/spark-local/examples/src/main/resources/people.json")
  2. rdd: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
  3.  
  4. scala> rdd.show
  5. +----+-------+
  6. | age| name|
  7. +----+-------+
  8. |null|Michael|
  9. | | Andy|
  10. | | Justin|
  11. +----+-------+
  1. SQL风格语法
    ##转化成sql去执行

    scala> rdd.createTempView("user") //view是table的查询结果,只能查不能改
  2. scala> spark.sql("select * from user").show
  3. +----+-------+
  4. | age| name|
  5. +----+-------+
  6. |null|Michael|
  7. | | Andy|
  8. | | Justin|
  9. +----+-------+
  10. scala> spark.sql("select * from user where age is not null").show
  11. +---+------+
  12. |age| name|
  13. +---+------+
  14. | | Andy|
  15. | |Justin|
  16. +---+------+

注意:普通临时view是Session范围内的,如果想应用范围内有效,可以使用全局临时表。使用全局临时表时需要全路径访问,如:global_temp.people

  1. scala> rdd.createGlobalTempView("emp") //提升为全局
  2. scala> spark.sql("select * from user where age is not null").show
  3. +---+------+
  4. |age| name|
  5. +---+------+
  6. | | Andy|
  7. | |Justin|
  8. +---+------+
  9.  
  10. scala> spark.sql("select * from emp where age is not null").show //sql默认从当前session中查找,所以查询时需要加上global_temp
  11. org.apache.spark.sql.AnalysisException: Table or view not found: emp; line pos
  12. scala> spark.sql("select * from global_temp.emp where age is not null").show
  13. +---+------+
  14. |age| name|
  15. +---+------+
  16. | | Andy|
  17. | |Justin|
  18. +---+------+

 ② 以面向对象方式访问;DSL风格语法 模仿面向对象的方式

  1. scala> rdd.printSchema
  2. root
  3. |-- age: long (nullable = true)
  4. |-- name: string (nullable = true)
  5.  
  6. scala> rdd.select("age").show
  7. +----+
  8. | age|
  9. +----+
  10. |null|
  11. | |
  12. | |
  13. +----+
  14.  
  15. scala> rdd.select($"age"+).show
  16. +---------+
  17. |(age + )|
  18. +---------+
  19. | null|
  20. | |
  21. | |
  22. +---------+

代码方式:

方式一:通过 case class 创建 DataFrames(反射)

  1. import org.apache.spark.SparkConf
  2. import org.apache.spark.sql.{DataFrame, SparkSession}
  3. object TestSparkSql {
  4. def main(args: Array[String]): Unit = {
  5. val conf: SparkConf = new SparkConf().setAppName("SparkSql").setMaster("local[*]")
  6. val sc: SparkSession = SparkSession.builder().config(conf).enableHiveSupport().getOrCreate()
  7. // 将本地的数据读入 RDD, 并将 RDD 与 case class 关联
  8. val peopleRdd = sc.sparkContext.textFile("file:\\F:\\Input\\people.txt")
  9. .map(line => People(line.split(",")(0),line.split(",")(1).trim.toInt))
  10. import sc.implicits._
  11. // 将RDD 转换成 DataFrames
  12. val df: DataFrame = peopleRdd.toDF
  13. //将DataFrames创建成一个临时的视图
  14. df.createOrReplaceTempView("people")
  15. sc.sql("select * from people").show() //使用SQL语句进行查询
  16. sc.stop()
  17. }
  18. }
  19. //定义case class,相当于表结构
  20. case class People(var name: String, var age: Int)

说明:

① textFile默认是从hdfs读取文件; 本地文件读取 sc.textFile("路径"),在路径前面加上file:// 表示从本地文件系统读

② textFile可直接读取多个文件夹(嵌套)下的多个数据文件,如上边路径可写成  "file:\\F:\\Input"  读取这个目录下多个文件

方式二:通过 structType 创建 DataFrames(编程接口),测试代码如下

  1. import org.apache.spark.SparkConf
  2. import org.apache.spark.rdd.RDD
  3. import org.apache.spark.sql.types.{IntegerType, StringType, StructField, StructType}
  4. import org.apache.spark.sql.{DataFrame, SparkSession}
  5. object TestSparkSql {
  6. def main(args: Array[String]): Unit = {
  7. val conf: SparkConf = new SparkConf().setAppName("SparkSql").setMaster("local[*]")
  8. val sc: SparkSession = SparkSession.builder().config(conf).enableHiveSupport().getOrCreate()
  9. // 将本地的数据读入 RDD
  10. val peopleRdd = sc.sparkContext.textFile("file:\\F:\\Input")
  11. // 将 RDD 数据映射成 Row,需要 import org.apache.spark.sql.Row
  12. import org.apache.spark.sql.Row
  13. val rowRDD: RDD[Row] = peopleRdd.map(line => {
  14. val fields = line.split(",")
  15. Row(fields(0), fields(1).trim.toInt)
  16. })
  17. val structType: StructType = StructType(
  18. //字段名,字段类型,是否可以为空
  19. StructField("name", StringType, true) ::
  20. StructField("age", IntegerType, true) :: Nil
  21. )
  22. //将DataFrames创建成一个临时的视图
  23. val df: DataFrame = sc.createDataFrame(rowRDD,structType)
  24. df.createTempView("people")
  25. sc.sql("select * from people").show() //使用SQL语句进行查询
  26. sc.stop()
  27. }
  28. }

方式三:读取json文件

people.json 必须是在一行:

  1. {"name":"swenna","age":18}
  2. {"name": "kk","age":20}
  1. //读取json数据
  2. import org.apache.spark.SparkConf
  3. import org.apache.spark.sql.{DataFrame, SparkSession}
  4. object TestSparkSql {
  5. def main(args: Array[String]): Unit = {
  6. val conf: SparkConf = new SparkConf().setAppName("SparkSql").setMaster("local[*]")
  7. val sc: SparkSession = SparkSession.builder().config(conf).enableHiveSupport().getOrCreate()
  8. // 将本地的数据读入 RDD
  9. val df: DataFrame = sc.read.json("file:\\F:\\Input\\people.json")
  10.  
  11. //将DataFrames创建成一个临时的视图
  12. df.createOrReplaceTempView("people")
  13. sc.sql("select * from people").show() //使用SQL语句进行查询
  14. sc.stop()
  15. }
  16. }

RDD转成DF

注意:如果需要RDD与DF或者DS之间操作,那么都需要引入 import spark.implicits._  【spark不是包名,而是sparkSession对象的名称】

前置条件:导入隐式转换并创建一个RDD

  1. scala> import spark.implicits._ spark对象中的隐式转换规则,而不是导入包名
  2. import spark.implicits._
  3. scala> val df = rdd.toDF("id", "name")
  4. df: org.apache.spark.sql.DataFrame = [id: bigint, name: string]
  5.  
  6. scala> df.show
  7. +----+-------+
  8. | id| name|
  9. +----+-------+
  10. |null|Michael|
  11. | | Andy|
  12. | | Justin|
  13. +----+-------+
  14. scala> df.createTempView("Student")
  15.  
  16. scala> spark.sql("select * from student").show
  1. scala> val x = sc.makeRDD(List(("a",), ("b",), ("c", )))
  2.  
  3. scala> x.collect
  4. res36: Array[(String, Int)] = Array((a,), (b,), (c,))
  5.  
  6. scala> x.toDF("name", "count")
  7. res37: org.apache.spark.sql.DataFrame = [name: string, count: int]
  8.  
  9. scala> val y = x.toDF("name", "count")
  10. y: org.apache.spark.sql.DataFrame = [name: string, count: int]
  11.  
  12. scala> y.show
  13. +----+-----+
  14. |name|count|
  15. +----+-----+
  16. | a | |
  17. | b | |
  18. | c | |
  19. +----+-----+

DF--->RDD  直接调用rdd即可

  1. scala> y.rdd.collect
  2. res46: Array[org.apache.spark.sql.Row] = Array([a,], [b,], [c,])
  3. scala> df.rdd.collect
  4. res49: Array[org.apache.spark.sql.Row] = Array([null,Michael], [,Andy], [,Justin])

RDD转换为DataSet

SparkSQL能够自动将包含有case类的RDD转换成DataFrame,case类定义了table的结构,case类属性通过反射变成了表的列名。Case类可以包含诸如Seqs或者Array等复杂的结构。     DataSet是具有强类型的数据集合,需要提供对应的类型信息。

  1. scala> case class People(age: BigInt, name: String)
  2. defined class People
  3. scala> rdd.collect
  4. res77: Array[org.apache.spark.sql.Row] = Array([null,Michael], [,Andy], [,Justin])
  5. scala> val ds = rdd.as[People]
  6. ds: org.apache.spark.sql.Dataset[People] = [age: bigint, name: string]
  7. scala> ds.collect
  8. res31: Array[People] = Array(People(null,Michael), People(,Andy), People(,Justin))
  1. scala> case class Person(name: String, age: Long)
  2. defined class Person
  3.  
  4. scala> val caseclassDS = Seq(Person("kris", )).toDS()
  5. caseclassDS: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]
  6.  
  7. scala> caseclassDS.show
  8. +----+---+
  9. |name|age|
  10. +----+---+
  11. |kris| |
  12. +----+---+
  13. scala> caseclassDS.collect
  14. res51: Array[Person] = Array(Person(kris,))

通过textFile方法创建rdd并转DS

  1. scala> val textFileRDD = sc.textFile("/opt/module/spark/spark-local/examples/src/main/resources/people.txt")
  2. scala> textFileRDD.collect
  3. res78: Array[String] = Array(Michael, , Andy, , Justin, )
  4. scala> case class Person(name: String, age: Long)
  5. defined class Person
  6.  
  7. scala> textFileRDD.map(x=>{val rddMap = x.split(","); Person(rddMap(), rddMap().trim.toInt)}).toDS
  8. res80: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]

DS ----> RDD 调用rdd方法即可

  1. scala> val DS = Seq(Person("Andy", )).toDS() 用这种方式可创建一个DataSet
  2. DS: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]
  3.  
  4. scala> ds.collect
  5. res76: Array[People] = Array(People(null,Michael), People(,Andy), People(,Justin))
  6. scala> ds.rdd.collect
  7. res75: Array[People] = Array(People(null,Michael), People(,Andy), People(,Justin))

DF ---> DS

spark.read.json(“ path ”)即是DataFrame类型;

  1. scala> df.collect
  2. res72: Array[org.apache.spark.sql.Row] = Array([null,Michael], [,Andy], [,Justin])
  3. scala> case class Student(id: BigInt, name: String)
  4. defined class Student
  5. scala> df.as[Student]
  6. res69: org.apache.spark.sql.Dataset[Student] = [id: bigint, name: string]

DS-->DF

这种方法就是在给出每一列的类型后,使用as方法,转成Dataset,这在数据类型是DataFrame又需要针对各个字段处理时极为方便。在使用一些特殊的操作时,一定要加上 import spark.implicits._ 不然toDF、toDS无法使用。

  1. scala> ds.collect
  2. res73: Array[People] = Array(People(null,Michael), People(,Andy), People(,Justin))
  3.  
  4. scala> ds.toDF
  5. res74: org.apache.spark.sql.DataFrame = [age: bigint, name: string]

三者的共性

(1)RDD、DataFrame、Dataset全都是spark平台下的分布式弹性数据集,为处理超大型数据提供便利;

(2)三者都有惰性机制,在进行创建、转换,如map方法时,不会立即执行,只有在遇到Action如foreach时,三者才会开始遍历运算;

(3)三者有许多共同的函数,如filter,排序等;

(4)在对DataFrame和Dataset进行操作许多操作都需要这个包:import spark.implicits._(在创建好SparkSession对象后尽量直接导入)

互相转化

RDD关心数据,DataFrame关心结构,DataSet关心类型;

  ① 将RDD转换为DataFrame,需要增加结构信息,所以调用toDF方法,需要增加结构;

  ② 将RDD转换为DataSet,需要增加结构和类型信息,所以需要转换为指定类型后,调用toDS方法;

  ③ 将DataFrame转换为DataSet时,因为已经包含结构信息,只有增加类型信息就可以,所以调用as[类型]

  ④因为DF中本身包含数据,所以转换为RDD时,直接调用rdd即可;

  ⑤因为DS中本身包含数据,所以转换为RDD时,直接调用rdd即可;

  ⑥因为DS本身包含数据结构信息,所以转换为DF时,直接调用toDF即可

三者的区别

联系:RDD、DataFrame、DataSet三者的联系是都是spark当中的一种数据类型,RDD是SparkCore当中的,DataFrame和DataSet都是SparkSql中的,它俩底层都基于RDD实现的;

区别:RDD 优点: ①编译时类型安全 ;②面向对象的编程风格 ; ③直接通过类名点的方式来操作数据; 缺点是通信or IO操作都需要序列化和反序列化的性能开销 ,比较耗费性能; GC的性能开销 ,频繁的创建和销毁对象, 势必会增加GC;

DataFrame引入了schema和off-heap堆外内存不会频繁GC,减少了内存的开销; 缺点是类型不安全;

DataSet结合了它俩的优点并且把缺点给屏蔽掉了;

1. RDD: ① RDD一般和spark mlib同时使用; ② RDD不支持sparksql操作

2. DataFrame:

  1)与RDD和Dataset不同,DataFrame每一行的类型固定为Row,每一列的值没法直接访问,只有通过解析才能获取各个字段的值,

  1. testDF.foreach{
  2. line =>
  3. val col1=line.getAs[String]("col1")
  4. val col2=line.getAs[String]("col2")
  5. }

  2)DataFrame与Dataset一般不与spark mlib同时使用

  3)DataFrame与Dataset均支持sparksql的操作,比如select,groupby之类,还能注册临时表/视窗,进行sql语句操作,如:dataDF.createOrReplaceTempView("tmp")

    spark.sql("select  ROW,DATE from tmp where DATE is not null order by DATE").show(100,false)

  4)DataFrame与Dataset支持一些特别方便的保存方式,比如保存成csv,可以带上表头,这样每一列的字段名一目了然

  1. val saveoptions = Map("header" -> "true", "delimiter" -> "\t", "path" -> "hdfs://hadoop102:9000/test") //保存
  2. datawDF.write.format("com.atguigu.spark.csv").mode(SaveMode.Overwrite).options(saveoptions).save() //读取
  3.  
  4. val options = Map("header" -> "true", "delimiter" -> "\t", "path" -> "hdfs://hadoop102:9000/test")
  5. val datarDF= spark.read.options(options).format("com.atguigu.spark.csv").load()
  6. 利用这样的保存方式,可以方便的获得字段名和列的对应,而且分隔符(delimiter)可以自由指定。

3. Dataset:

  1)Dataset和DataFrame拥有完全相同的成员函数,区别只是每一行的数据类型不同。

  2)DataFrame也可以叫Dataset[Row],每一行的类型是Row,不解析,每一行究竟有哪些字段,各个字段又是什么类型都无从得知,只能用上面提到的getAS方法或者模式匹配拿出特定字段。而Dataset中,每一行是什么类型是不一定的,在自定义了case class之后可以很自由的获得每一行的信息

  1. case class Coltest(col1:String,col2:Int)extends Serializable //定义字段名和类型
  2. /**
  3. rdd
  4. ("a", 1)
  5. ("b", 1)
  6. ("a", 1)
  7. **/
  8. val test: Dataset[Coltest]=rdd.map{line=>
  9. Coltest(line._1,line._2)
  10. }.toDS
  11. test.map{
  12. line=>
  13. println(line.col1)
  14. println(line.col2)
  15. }

可以看出,Dataset在需要访问列中的某个字段时是非常方便的,然而,如果要写一些适配性很强的函数时,如果使用Dataset,行的类型又不确定,可能是各种case class,无法实现适配,这时候用DataFrame即Dataset[Row]就能比较好的解决问题

IDEA创建SparkSQL程序

  1. object TestSparkSql {
  2. def main(args: Array[String]): Unit = {
  3. //创建配置对象
  4. val conf: SparkConf = new SparkConf().setAppName("SQL").setMaster("local[*]")
  5. //创建环境对象
  6. val sparkSession: SparkSession = SparkSession.builder().config(conf).getOrCreate()
  7. //导入隐式转换
  8. import sparkSession.implicits._
  9.  
  10. //执行操作
  11. // TODO 创建DataFrame
  12. val df: DataFrame = sparkSession.read.json("input/input.json")
  13. df.createTempView("user")
  14. sparkSession.sql("select * from user")
  15. //df.show()
  16. // TODO 创建DataSet
  17. val ds: Dataset[Employ] = Seq(Employ("jing", 18)).toDS()
  18. //ds.show()
  19. // TODO 将DataFrame转换为DataSet
  20. val dfToDs: Dataset[Employ] = df.as[Employ]
  21. dfToDs.foreach(x => {
  22. println(x.name + "\t" + x.age)
  23. })
  24. //TODO 将RDD转换为DataSet
  25. val rdd: RDD[(String, Int)] = sparkSession.sparkContext.makeRDD(Array(("aa", 19)))
  26. val employRdd: RDD[Employ] = rdd.map {
  27. case (name, age) => Employ(name, age)
  28. }
  29. //employRdd.toDS().show()
  30.  
  31. // TODO 将RDD转换为DataFrame
  32. //rdd.toDF().show()
  33. val rddToDf: DataFrame = sparkSession.sparkContext.makeRDD(Array(("kris", 18))).toDF("username", "age")
  34.  
  35. //TODO 将DataFrame转换为RDD[Row]
  36. df.rdd.foreach(row => {
  37. println(row.getLong(0)+ "," + row.getString(1))
  38. })
  39.  
  40. // TODO 将DataSet转换为RDD[类型]
  41. val dsToRdd: RDD[Employ] = df.as[Employ].rdd
  42. sparkSession.stop()
  43. }
  44. }
  45. case class Employ(name: String, age: BigInt)

用户自定义函数

Spark SQL数据的加载与保存

通用加载/保存方法 load和save

通用的读写方法是  sparkSql只读这parquet file这种类型的文件;  否则要改变它的文件类型需要加.format 
加上format("json");输出也是这个类型

  1. scala>val df = spark.read.load("/opt/module/spark/spark-local/examples/src/main/resources/users.parquet").show
  2.  
  3. scala>df.select("name", " color").write.save("user.parquet") //保存数据
  4. java.lang.RuntimeException: file:/opt/module/spark/spark-local/examples/src/main/resources/people.json is not a Parquet file.
  5. load读取json数据
  6. scala> spark.read.format("json").load("/opt/module/spark/spark-local/examples/src/main/resources/people.json").show
  7.  
  8. df.write.format("json").save("/..")
  9.  
  10. spark.read.format("json").mode("overwrite").save("/..json")

MySQL  Spark之读取MySQL数据的方式

Spark SQL可以通过JDBC从关系型数据库中读取数据的方式创建DataFrame,通过对DataFrame一系列的计算后,还可以将数据再写回关系型数据库中。

可在启动shell时指定相关的数据库驱动路径,或者将相关的数据库驱动放到spark的类路径下。

  1. [kris@hadoop101 jars]$ cp /opt/software/mysql-connector-java-5.1.27/mysql-connector-java-5.1.27-bin.jar ./
  2.  
  3. scala> val connectionProperties = new java.util.Properties()
  4. connectionProperties: java.util.Properties = {}
  5.  
  6. scala> connectionProperties.put("user", "root")
  7. res0: Object = null
  8.  
  9. scala> connectionProperties.put("password", "123456")
  10. res1: Object = null
  11.  
  12. scala> val jdbcDF2 = spark.read.jdbc("jdbc:mysql://hadoop101:3306/rdd", "test", connectionProperties)
  13. jdbcDF2: org.apache.spark.sql.DataFrame = [id: int, name: string]
  14.  
  15. scala> jdbcDF2.show
  16. +---+-------+
  17. | id| name|
  18. +---+-------+
  19. | 1| Google|
  20. | 2| Baidu|
  21. | 3| Ali|
  22. | 4|Tencent|
  23. | 5| Amazon|
  24. +---+-------+
  25.  
  26. jdbcDF2.write.mode("append").jdbc("jdbc:mysql://hadoop101:3306/rdd", "test", connectionProperties)
  27.  
  28. scala> val rdd = sc.makeRDD(Array((6, "FaceBook")))
  29. rdd: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[] at makeRDD at <console>:24
  30.  
  31. scala> rdd.toDF("id", "name")
  32. res5: org.apache.spark.sql.DataFrame = [id: int, name: string]
  33.  
  34. scala> val df = rdd.toDF("id", "name")
  35. df: org.apache.spark.sql.DataFrame = [id: int, name: string]
  36.  
  37. scala> df.show
  38. +---+--------+
  39. | id| name|
  40. +---+--------+
  41. | 6|FaceBook|
  42. +---+--------+
  43. scala> df.write.mode("append").jdbc("jdbc:mysql://hadoop101:3306/rdd", "test", connectionProperties)
  44. scala> jdbcDF2.show
  45. +---+--------+
  46. | id| name|
  47. +---+--------+
  48. | 1| Google|
  49. | 2| Baidu|
  50. | 3| Ali|
  51. | 4| Tencent|
  52. | 5| Amazon|
  53. | 6|FaceBook|
  54. +---+--------+

代码

  1. import java.util.Properties
  2. import org.apache.spark.SparkConf
  3. import org.apache.spark.sql.{DataFrame, SparkSession}
  4.  
  5. object TestSparkSql {
  6. def main(args: Array[String]): Unit = {
  7. val conf: SparkConf = new SparkConf().setAppName("SparkSql").setMaster("local[*]")
  8. val sc: SparkSession = SparkSession.builder().config(conf).enableHiveSupport().getOrCreate()
  9. //method01(sc)
  10. method02(sc)
  11. //method03(sc)
  12. }
  13. /**
  14. * 方式一:不指定查询条件
  15. * 所有的数据由RDD的一个分区处理,如果你这个表数据量很大,表的所有数据都是由RDD的一个分区处理,很可能会出现OOM
  16. * @param sc
  17. */
  18. def method01(sc: SparkSession): Unit = {
  19. // 将本地的数据读入 RDD
  20. val url = "jdbc:mysql://hadoop101/company?"
  21. val table = "staff"
  22. val prop = new Properties()
  23. prop.setProperty("user", "root")
  24. prop.setProperty("password", "123456")
  25. //需要传入Mysql的URL、表名、properties(连接数据库的用户名密码)
  26. val df: DataFrame = sc.read.jdbc(url, table, prop)
  27. println(df.count()) //
  28. println(df.rdd.partitions.size) //
  29. df.createOrReplaceTempView("staff")
  30. sc.sql("select * from staff where id <=2").show()
  31. //df.show()
  32. sc.stop()
  33. }
  34. /**
  35. * 方式二:指定数据库字段的范围
  36. * 通过lowerBound和upperBound 指定分区的范围
  37. * 通过columnName 指定分区的列(只支持整形)
  38. * 通过numPartitions 指定分区数量 (不宜过大)
  39. * 说明:将表的数据分布到RDD的几个分区中,分区的数量由numPartitions参数决定,在理想情况下,每个分区处理相同数量的数据,我们在使用的时候不建议将这个值设置的比较大,因为这可能导致数据库挂掉!这个函数的缺点就是只能使用整形数据字段作为分区关键字。
  40. * @param sc
  41. */
  42. def method02(sc: SparkSession): Unit = {
  43. val lowerBound = 1
  44. val upperBound = 100000
  45. val numPartitions = 5
  46. val url = "jdbc:mysql://hadoop101/company?user=root&password=123456"
  47. val prop = new Properties()
  48. val df: DataFrame = sc.read.jdbc(url, "staff", "id", lowerBound, upperBound,numPartitions,prop)
  49.  
  50. df.show()
  51. println(df.count())
  52. println(df.rdd.partitions.length) //5个分区
  53. }
  54.  
  55. /**
  56. * 方式三:根据任意字段进行分区
  57. * 通过predicates将数据根据score分为2个区
  58. * 基于前面两种方法的限制,Spark还提供了根据任意字段进行分区的方法;rdd的分区数量就等于predicates.length
  59. * @param sc
  60. */
  61. def method03(sc: SparkSession) = {
  62. val predicates = Array[String]("id <=2", "id > 1 and id < 3") //2个分区
  63. val url = "jdbc:mysql://hadoop101/company?user=root&password=123456"
  64. val prop = new Properties()
  65. val df: DataFrame = sc.read.jdbc(url,"staff",predicates,prop)
  66. println(df.count()) //
  67. println(df.rdd.partitions.length) //
  68. df.show()
  69. }
  70.  
  71. }
  72.  
  73. 方式四: 通过load获取,和方式二类似
  74. options函数支持urldriverdbtablepartitionColumnlowerBoundupperBound以及numPartitions选项,与方法二的参数一致。
    其内部实现原理部分和方法二大体一致。同时load方法还支持jsonorc等数据源的读取。
  75. val df: DataFrame = sc.read.format("jdbc").options(Map ("url" -> url, "dbtable" -> "staff")).load()
  76. 加载条件查询后的数据,报错: Every derived table must have its own alias,这句话的意思是说每个派生出来的表都必须有一个自己的别名,加了一个没有别名即可

  77. val df: DataFrame = sc.read.format("jdbc").options(Map ("url" -> url, "dbtable" -> "(select s1.id,s2.name,s1.age from stu1 s1 join stu2 s2 on s1.id = s2.id ) stu")).load()

Hive   Spark之HiveSupport连接(spark-shell和IDEA)

  1. spark.sparkContext.setLogLevel("WARN") //设置日志输出级别

Apache Hive是Hadoop上的SQL引擎,Spark SQL编译时可以包含Hive支持,也可以不包含。包含Hive支持的Spark SQL可以支持Hive表访问、UDF(用户自定义函数)以及Hive查询语言(HQL)等。spark-shell默认是Hive支持的;代码中是默认不支持的,需要手动指定(加一个参数即可)。

如果要使用内嵌的Hive,什么都不用做,直接用就可以了。

可以修改其数据仓库地址,参数为:--conf spark.sql.warehouse.dir=./wear

  1. scala> spark.sql("create table emp(name String, age Int)").show
  2. 19/04/11 01:10:17 WARN HiveMetaStore: Location: file:/opt/module/spark/spark-local/spark-warehouse/emp specified for non-external table:emp
  3.  
  4. scala> spark.sql("load data local inpath '/opt/module/spark/spark-local/examples/src/main/resources/people.txt' into table emp").show
  5.  
  6. scala> spark.sql("show tables").show
  7. +--------+---------+-----------+
  8. |database|tableName|isTemporary|
  9. +--------+---------+-----------+
  10. | default| emp| false|
  11. +--------+---------+-----------+
  12. scala> spark.sql("select * from emp").show
  13.  
  14. /opt/module/spark/spark-local/spark-warehouse/emp
  15. [kris@hadoop101 emp]$ ll
  16. -rwxr-xr-x. 1 kris kris 32 4 11 01:10 people.txt

外部Hive应用

  1. [kris@hadoop101 spark-local]$ rm -rf metastore_db/ spark-warehouse/
  2.  
  3. [kris@hadoop101 conf]$ cp hive-site.xml /opt/module/spark/spark-local/conf/
  4.  
  5. [kris@hadoop101 spark-local]$ bin/spark-shell
  6. scala> spark.sql("show tables").show
  7. +--------+--------------------+-----------+
  8. |database| tableName|isTemporary|
  9. +--------+--------------------+-----------+
  10. | default| bigtable| false|
  11. | default| business| false|
  12. | default| dept| false|
  13. | default| dept_partition| false|
  14. | default| dept_partition2| false|
  15. | default| dept_partitions| false|
  16. | default| emp| false|
  17. ...
  18.  
  19. [kris@hadoop101 spark-local]$ bin/spark-sql
  20. log4j:WARN No appenders could be found for logger (org.apache.hadoop.util.Shell).
  21. log4j:WARN Please initialize the log4j system properly.
  22. spark-sql (default)> show tables;

代码中操作Hive

log4j.properties

  1. log4j.rootLogger=INFO, stdout
  2. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
  3. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
  4. log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
  5. log4j.appender.logfile=org.apache.log4j.FileAppender
  6. log4j.appender.logfile.File=target/spring.log
  7. log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
  8. log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

拷贝Hadoop中core-site.xml、hdfs-site.xml,Hive中hive-site.xml三个文件到resources中(也可以只拷贝hive-site.xml),集群环境把hive的配置文件要发到$SPARK_HOME/conf目录下;

Maven所依赖的jar包:

  1. <dependencies>
  2. <dependency>
  3. <groupId>mysql</groupId>
  4. <artifactId>mysql-connector-java</artifactId>
  5. <version>5.1.27</version>
  6. </dependency>
  7. <!--spark操作Hive所需引入的包 spark版本-->
  8. <dependency>
  9. <groupId>org.apache.spark</groupId>
  10. <artifactId>spark-hive_2.11</artifactId>
  11. <version>2.1.1</version>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.apache.spark</groupId>
  15. <artifactId>spark-core_2.11</artifactId>
  16. <version>2.1.1</version>
  17. </dependency>
  18.  
  19. <dependency>
  20. <groupId>org.apache.spark</groupId>
  21. <artifactId>spark-sql_2.11</artifactId>
  22. <version>2.1.1</version>
  23. </dependency>
  24.  
  25. <dependency>
  26. <groupId>org.apache.spark</groupId>
  27. <artifactId>spark-streaming_2.11</artifactId>
  28. <version>2.1.1</version>
  29. </dependency>
  30.  
  31. </dependencies>
  1. val sparkSession = SparkSession.builder().config(conf).enableHiveSupport().getOrCreate()
  2. 支持hive

 测试:

  1. import org.apache.spark.SparkConf
  2. import org.apache.spark.sql.SparkSession
  3. object TestSparkSql {
  4. def main(args: Array[String]): Unit = {
  5. val conf: SparkConf = new SparkConf().setAppName("SparkSql").setMaster("local[*]")
  6. val sc: SparkSession = SparkSession.builder().config(conf).enableHiveSupport().getOrCreate()
  7. sc.sql("show tables").show()
  8. sc.stop()
  9. }
  10. }

SparkSQL 的元数据

1.1元数据的状态

SparkSQL 的元数据的状态有两种:

1、in_memory,用完了元数据也就丢了

2、hive , 通过hive去保存的,也就是说,hive的元数据存在哪儿,它的元数据也就存在哪儿。

换句话说,SparkSQL的数据仓库在建立在Hive之上实现的。我们要用SparkSQL去构建数据仓库的时候,必须依赖于Hive。

2.2Spark-SQL脚本

如果用户直接运行bin/spark-sql命令。会导致我们的元数据有两种状态:

1、in-memory状态:如果SPARK-HOME/conf目录下没有放置hive-site.xml文件,元数据的状态就是in-memory

2、hive状态:如果我们在SPARK-HOME/conf目录下放置了,hive-site.xml文件,那么默认情况下,spark-sql的元数据的状态就是hive.

伴生对象相当于static,可直接类名.
给类起别名,相当于属性使用type ..

  1. spark.sql("select age, addName(name) from user").show
  2.  
  3. scala> case class tbStock(ordernumber:String,locationid:String,dateid:String) extends Serializable
  4. scala> val tbStockRdd = spark.sparkContext.textFile("/opt/module/datas/sparkData/tbStock.txt")
  5. tbStockRdd: org.apache.spark.rdd.RDD[String] = /opt/module/datas/sparkData/tbStock.txt MapPartitionsRDD[] at textFile at <console>:23
  6. scala> val tbStockDS = tbStockRdd.map(_.split("\t")).map(x => tbStock(x(0), x(1), x(2))).toDS
  7. tbStockDS: org.apache.spark.sql.Dataset[tbStock] = [ordernumber: string, locationid: string ... 1 more field]
  8.  
  9. scala> tbStockDS.show
  10. +-----------+----------+----------+
  11. |ordernumber|locationid| dateid|
  12. +-----------+----------+----------+
  13. | lj111| jd| 2018-3-13|
  14. | lj112| jd| 2018-2-13|
  15. | lj113| jd| 2019-1-13|
  16. | lj114| jd| 2019-3-13|
  17. | lj115| jd| 2018-9-13|
  18. | lj116| jd|2018-11-13|
  19. | lj117| jd|2017-12-13|
  20. | lj118| jd| 2017-5-13|
  21. +-----------+----------+----------+
  22.  
  23. scala> case class tbStockDetail(ordernumber:String, rownum:Int, itemid:String, number:Int, price:Double, amount:Double) extends Serializable
  24. defined class tbStockDetail
  25. scala> val tbStockDetailRdd = spark.sparkContext.textFile("/opt/module/datas/sparkData/tbStockDetail.txt")
  26. tbStockDetailRdd: org.apache.spark.rdd.RDD[String] = /opt/module/datas/sparkData/tbStockDetail.txt MapPartitionsRDD[] at textFile at <console>:23
  27. scala> val tbStockDetailDS = tbStockDetailRdd.map(_.split("\t")).map(x => tbStockDetail(x(0), x(1).trim().toInt, x(2), x(3).trim().toInt, x(4).trim().toDouble,x(5).trim().toDouble)).toDS
  28. tbStockDetailDS: org.apache.spark.sql.Dataset[tbStockDetail] = [ordernumber: string, rownum: int ... 4 more fields]
  29.  
  30. scala> tbStockDetailDS.show
  31. +-----------+------+------+------+-----+------+
  32. |ordernumber|rownum|itemid|number|price|amount|
  33. +-----------+------+------+------+-----+------+
  34. | lj111| 12|item11| 10|100.0| 300.0|
  35. | lj112| 12|item12| 10|100.0| 200.0|
  36. | lj113| 12|item13| 10|100.0| 300.0|
  37. | lj114| 12|item14| 10|100.0| 100.0|
  38. | lj115| 12|item15| 10|100.0| 300.0|
  39. | lj116| 12|item16| 10|100.0| 700.0|
  40. | lj117| 12|item17| 10|100.0| 600.0|
  41. | lj118| 12|item18| 10|100.0| 500.0|
  42. +-----------+------+------+------+-----+------+
  1. tbstocktbstockdetail--amount tbdate
  2. 计算所有订单中每年的销售单数、销售总额
  3. 三个表连接后以count(distinct a.ordernumber)计销售单数,sum(b.amount)计销售总额
  4. select
  5. theyear,
  6. count(tbstock.ordernumber),
  7. sum(tbstockdetail.amount)
  8. from tbstock join tbstockdetail on tbstock.ordernumber = tbstockdetail.ordernumber
  9. join tbdate on tbdate.dateid = tbstock.dateid
  10. group by tbdate.theyear
  11. order by tbdate.theyear;
  12.  
  13. 统计每年最大金额订单的销售额:
  14. 统计每个订单一共有多少销售额
  15. select
  16. a.dateid,
  17. a.ordernumber,
  18. sum(b.amount) sumAmount
  19. from tbstock a join tbstockdetail b on a.ordernumber = b.ordernumber
  20. group by a.dateid, a.ordernumber
  21.  
  22. select
  23. theyear,
  24. max(c.sumAmount) sumOfAmount
  25. from tbdate join (select a.dateid, a.ordernumber, sum(b.amount) sumAmount
  26. from tbstock a join tbstockdetail b on a.ordernumber = b.ordernumber
  27. group by a.dateid, a.ordernumber)c on tbdate.dateid = c.dateid
  28. group by tbdate.theyear order by tbdate.theyear desc
  29.  
  30. 计算所有订单中每年最畅销货品
  31. 目标:统计每年最畅销货品(哪个货品销售额amount在当年最高,哪个就是最畅销货品)
  32. 1求出每年每个货品的销售额
  33. 每年 tbdate.theyear
  34. 货品tbstockdetail.itemid
  35. 销售额amount在当年最高
  36. select tbdate.theyear, tbstockdetail.itemid, sum(tbstockdetail.amount) sumAmount
  37. from tbdate join tbstock on tbdate.dateid = tbstock.dateid
  38. join tbstockdetail on tbstockdetail.ordernumber = tbstock.ordernumber
  39. group by tbdate.theyear, tbstockdetail.itemid
  40.  
  41. 2在第一步的基础上,统计每年 所有 货品中的最大金额
  42. select aa.theyear, max(sumAmount) maxAmount
  43. from (
  44. select tbdate.theyear, tbstockdetail.itemid, sum(tbstockdetail.amount) sumAmount
  45. from tbdate
  46. join tbstock on tbdate.dateid = tbstock.dateid
  47. join tbstockdetail on tbstockdetail.ordernumber = tbstock.ordernumber
  48. group by tbdate.theyear, tbstockdetail.itemid)aa
  49. group by aa.theyear
  50.  
  51. 用最大销售额和统计好的每个货品的销售额join,以及用年join,集合得到最畅销货品那一行信息
  52. 每年每个货品的销售额 join 每年所有货品中的最大金额
  53. select distinct e.theyear, e.itemid, f.maxAmount
  54. from (
  55. select tbdate.theyear, tbstockdetail.itemid, sum(tbstockdetail.amount) sumAmount
  56. from tbdate
  57. join tbstock on tbdate.dateid = tbstock.dateid
  58. join tbstockdetail on tbstockdetail.ordernumber = tbstock.ordernumber
  59. group by tbdate.theyear, tbstockdetail.itemid)e join (select aa.theyear, max(sumAmount) maxAmount
  60. from (
  61. select tbdate.theyear, tbstockdetail.itemid, sum(tbstockdetail.amount) sumAmount
  62. from tbdate join tbstock on tbdate.dateid = tbstock.dateid
  63. join tbstockdetail on tbstockdetail.ordernumber = tbstock.ordernumber
  64. group by tbdate.theyear, tbstockdetail.itemid)aa
  65. group by aa.theyear)f on e.theyear = f.theyear and e.sumAmount = f.maxAmount
  66. order by e.theyear

SparkSQL的更多相关文章

  1. 踩坑事件:windows操作系统下的eclipse中编写SparkSQL不能从本地读取或者保存parquet文件

    这个大坑... .... 如题,在Windows的eclipse中编写SparkSQL代码时,编写如下代码时,一运行就抛出一堆空指针异常: // 首先还是创建SparkConf SparkConf c ...

  2. sparksql udf的运用----scala及python版(2016年7月17日前完成)

    问:udf在sparksql 里面的作用是什么呢? 答:oracle的存储过程会有用到定义函数,那么现在udf就相当于一个在sparksql用到的函数定义: 第二个问题udf是怎么实现的呢? regi ...

  3. spark-sql性能测试

    一,测试环境       1) 硬件环境完全相同:              包括:cpu/内存/网络/磁盘Io/机器数量等       2)软件环境:              相同数据       ...

  4. SparkSQL读取Hive中的数据

    由于我Spark采用的是Cloudera公司的CDH,并且安装的时候是在线自动安装和部署的集群.最近在学习SparkSQL,看到SparkSQL on HIVE.下面主要是介绍一下如何通过SparkS ...

  5. SparkSQL(源码阅读三)

    额,没忍住,想完全了解sparksql,毕竟一直在用嘛,想一次性搞清楚它,所以今天再多看点好了~ 曾几何时,有一个叫做shark的东西,它改了hive的源码...突然有一天,spark Sql突然出现 ...

  6. Spark入门实战系列--6.SparkSQL(上)--SparkSQL简介

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .SparkSQL的发展历程 1.1 Hive and Shark SparkSQL的前身是 ...

  7. Spark入门实战系列--6.SparkSQL(中)--深入了解SparkSQL运行计划及调优

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 1.1  运行环境说明 1.1.1 硬软件环境 线程,主频2.2G,10G内存 l  虚拟软 ...

  8. Spark入门实战系列--6.SparkSQL(下)--Spark实战应用

    [注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .运行环境说明 1.1 硬软件环境 线程,主频2.2G,10G内存 l  虚拟软件:VMwa ...

  9. 大数据——sparksql

    sparksql:http://www.cnblogs.com/shishanyuan/p/4723604.html?utm_source=tuicool spark on yarn :http:// ...

  10. SparkSql 不支持Date Format (支持Timestamp)

    最近项目中需要用到sparksql ,需要查询sql Date类型, 无奈,官方现阶段 1.6.0 还不支持Date类型,不过支持Timestamp类型,所以问题可以解决了. 1.解析 SimpleD ...

随机推荐

  1. hive group by聚合函数增强

    1.grouping sets grouping sets子句都可以根据UNION连接的多个GROUP BY查询进行逻辑表示 SELECT a,b,SUM(c)FROM tab1 GROUP BY a ...

  2. 试用VS2019正式版

    1.下载地址https://visualstudio.microsoft.com/zh-hans/downloads/2.安装,安装过程比较简单,直接下一步,下一步即可,现在的网速一般下载+安装要2个 ...

  3. CentOS Linux change IP Address

    1.change network card configure edit: vi /etc/sysconfig/network-scripts/ifcfg-eth0 ps:notice HWADDR! ...

  4. mysql—常用查询语句总结

    关于MySQL常用的查询语句 一查询数值型数据: ; 查询谓词:>,=,<,<>,!=,!>,!<,=>,=< 二查询字符串 SELECT * FROM ...

  5. VLAN原理解释

    转发至http://network.51cto.com/art/201409/450885.htm 为什么需要VLAN 1. 什么是VLAN? VLAN(Virtual LAN),翻译成中文是“虚拟局 ...

  6. 一些C++的语法

    一.类的析构函数 类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行. 析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何 ...

  7. python 去除html 超链接href 如何实现?

    今天持久男 在抓取数据的时候发现很多内容都加了锚文本, 这怎么办呢? 没办法只能通过工具解决 我是这样解决的: 例如: soup = BeautifulSoup('<p>Hello < ...

  8. 「HNOI 2019」白兔之舞

    一道清真的数论题 LOJ #3058 Luogu P5293 题解 考虑$ n=1$的时候怎么做 设$ s$为转移的方案数 设答案多项式为$\sum\limits_{i=0}^L (sx)^i\bin ...

  9. RestTemplate通过InputStreamResource上传文件

    需求:从ftp取文件并http调用某接口上传此文件 偷懒的话可以从ftp上取文件存到本地,再调用接口上传文件,如下 String ftpPath = "/ftp/path/file.bin& ...

  10. npx命令介绍

    什么是npx 第一次看到npx命令是在 babel 的文档里 Note: If you do not have a package.json, create one before installing ...