sparkSql学习之原理,DataFrame,DataSet及idea代码开发
1 目标
- 1、掌握sparksql原理
- 2、掌握DataFrame和DataSet数据结构和使用方式
- 3、掌握sparksql的应用程序开发
2、sparksql概述
2.1 sparksql前世今生
- shark是专门为spark准备的大规模数据仓库系统
- shark继承了hive大且复杂的代码,同时它也依赖于spark的版本
- 后期我们发现hive的复杂的代码逻辑限制了shark的发展
- 最后终止了对shark开发,把重点转移到了sparksql上
2.2 sparksql是什么
- Spark SQL is Apache Spark’s module for working with structured data.
- sparksql是apache spark的一个处理结构化数据的模块。
- 它提供了一个编程抽象叫做DataFrame并且作为分布式SQL查询引擎的作用。
3、sparksql四大特性
- 1、易整合
- 可以将sparksql与spark应用程序进行混合使用
- 并且可以使用java、scala、python、R等不同语言进行代码开发
- 2、统一的数据源访问
- sparksql可以使用一种相同的方式跟不同的外部数据源进行对接
- SparkSession.read.文件格式(“该格式文件的路径”)
- 3、兼容hive
- sparksql支持hivesql的语法
- 4、支持标准的数据库连接
- sparksql可以使用标准的jdbc或者是odbc连接上数据库
4、DataFrame
4.1 DataFrame发展
在spark1.3.0之前是没有dataFrame,只有一个schemaRDD,它是直接继承了RDD。在spark1.3.0之后把schema改为dataFrame,但是与schemaRDD有区别:它不在继承自rdd,而是自己实现了rdd的一些方法。后期也可以调用rdd这个方法把一个dataFrame转换成一个rdd。
4.2 DataFrame概念
在Spark中,DataFrame是一种以RDD为基础的分布式数据集,类似于传统数据库的二维表格,DataFrame带有Schema元信息,即DataFrame所表示的二维表数据集的每一列都带有名称和类型,但底层做了更多的优化
4.3 rdd与dataFrame优缺点对比
-
rdd
-
优点
- 1、编译时类型安全
- 就是后期再编译rdd代码的时候,会进行类型检查
- 2、具有面向对象的编程风格
- 可以使用对象调用方法的形式去操作对象
- 1、编译时类型安全
-
缺点
-
1、数据对象序列化和反序列化性能开销很大
spark任务是通过分布式计算实现的,后期大量的task在运行的时候,需要进行大量的数据网络传输,数据在传输的时候先要进行序列化,数据内容本身和数据结构信息都需要进行序列化,然后进行反序列化获取得到该对象。
-
2、大量的对象创建会带来大量的GC
在通过rdd进行编程的时候,可能会构建大量的对象,这些对象的构建都是在jvm堆以内。这个时候需要占用大量的内存空间,后期内存不足了,会导致频繁GC,GC发生时。这个时候所有的任务都是暂停,GC结束之后,任务又继续运行。后期代码要尽量减少GC(垃圾回收)。
-
-
-
dataFrame
-
dataFrame分别引入了schema(元数据信息)和off-heap(使用不在堆中的内存,直接使用操作系统层面上的内存)
-
优点
-
1、它引入了schema,这里的schema就是对数据结构的描述信息,后期这里数据在进行网络传输的时候,只需要序列化数据本身就可以了,数据的结构信息这一块是可以省略掉了,这样一来是减少数据的网络传输。性能是有得到提升。它是解决了rdd中数据对象序列化和反序列化性能开销很大这个缺点。
-
2、它引入了off-heap,不在使用堆中的内存去构建对象,这个时候是使用操作系统层面上的内存,
后期jvm堆中的内存就比较充足,不会频繁导致gc。它是解决了rdd大量的对象创建会带来大量的GC这个缺点。
-
-
缺点
- dataFrame分别引入了schema和off-heap解决了rdd的缺点,同时也丢失了rdd的优点
- 1、dataFrame编译时类型不安全
- 2、dataFrame不具备面向对象编程的风格
- dataFrame分别引入了schema和off-heap解决了rdd的缺点,同时也丢失了rdd的优点
-
5、读取数据源构建dataFrame
1、读取文本文件构建dataFrame
val df=spark.read.text("/person.txt")
//打印schema
df.printSchema
//展示数据
df.show
2、读取json文件构建dataFrame
val df=spark.read.json("/people.json")
//打印schema
df.printSchema
//展示数据
df.show
3、读取parquet列式存储文件构建dataFrame
val df=spark.read.parquet("/users.parquet")
//打印schema
df.printSchema
//展示数据
df.show
6、DatFrame常用操作
6.1 DSL风格语法
- dataFrame自身提供了一套api,可以通过这套api去操作它
val rdd1=sc.textFile("/person.txt")
val rdd2=rdd1.map(_.split(" "))
case class Person(id:Int,name:String,age:Int)
val rdd3=rdd2.map(x=>Person(x(0).toInt,x(1),x(2).toInt))
val personDF=rdd3.toDF
personDF.printSchema
personDF.show
personDF.select("name").show
personDF.select($"name").show
personDF.select(col("name")).show
personDF.select("name","age").show
personDF.select($"age"+1).show
personDF.filter($"age" > 30).show
personDF.filter($"age" > 30).count
personDF.groupBy("age").count.show
6.2 SQL风格语法
-
就是可以把dataFrame看成是一张表
-
需要把dataFrame注册成一张表
personDF.createTempView("person")
-
SparkSession.sql(sql语句)
spark.sql("select * from person").show spark.sql("select * from person where id=1").show spark.sql("select count(*) from person where age >30").show
-
7、DataSet
7.1 DataSet是什么
DataSet是分布式的数据集合,Dataset提供了强类型支持,也是在RDD的每行数据加了类型约束。DataSet是在Spark1.6中添加的新的接口。它集中了RDD的优点(强类型和可以用强大lambda函数)以及使用了Spark SQL优化的执行引擎。
7.2 dataSet和dataFrame互相转换
-
1、dataFrame转换成dataSet
- val ds=df.as[强类型]
-
2、dataSet转换成dataFrame
- val df=ds.toDF
-
补充
可以使用dataFrame和dataSet分别调用各自rdd方法,将其转换为一个rdd
7.3 如何构建dataSet
-
1、可以从一个已经存在的scala集合去构建
val ds=spark.createDataset(List(1,2,3,4)) val ds=List(1,2,3,4).toDS
-
2、可以从一个已经存在的rdd去构建
val ds=spark.createDataset(sc.textFile("/words.txt"))
-
3、dataFrame转换成dataSet
val ds=df.as[强类型]
-
4、通过一个dataSet转换生成一个新的dataSet
val ds2=ds1.map(x=>x+"_itcast")
8、通过IDEA开发sparksql程序实现将RDD转换成DataFrame
-
1、引入pom依赖
<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.11</artifactId> <version>2.1.3</version> </dependency>
8.1 利用反射机制将rdd转换成dataFrame
-
1、代码开发
package cn.itcast.sparksql import org.apache.spark.SparkContext import org.apache.spark.rdd.RDD import org.apache.spark.sql.{Column, DataFrame, SparkSession} //todo:利用反射将rdd转换成dataFrame(定义样例类) case class Person(id:Int,name:String,age:Int) object CaseClassSchema { def main(args: Array[String]): Unit = { //1、构建SparkSession val spark: SparkSession = SparkSession .builder() .appName("CaseClassSchema") .master("local[2]") .getOrCreate() //2、获取sparkContext对象 val sc: SparkContext = spark.sparkContext sc.setLogLevel("warn") //3、读取文件数据 val rdd1: RDD[Array[String]] = sc.textFile("E:\\person.txt").map(_.split(" ")) //4、将rdd与样例类进行关联 val personRDD: RDD[Person] = rdd1.map(x=>Person(x(0).toInt,x(1),x(2).toInt)) //5、将rdd转换成DataFrame //手动导入隐式转换 import spark.implicits._ val personDF: DataFrame = personRDD.toDF //6、打印schema personDF.printSchema() //--------------------DSL风格语法--------------start personDF.show() //默认展示20条数据,如果一个字符串超过了20个字符,进行截取,保留前20位 zhangsanxxxxxxxxxxxxxxxxxxxxxxxxx personDF.show(1) //获取第一条数据 println(personDF.first()) println(personDF.head()) //查询name字段的所有信息 personDF.select("name").show() personDF.select($"name").show() personDF.select(new Column("name")).show() personDF.select("name","age","id").show() //把age+1 personDF.select($"age",$"age"+1).show() //过滤出年龄大于30的用户 personDF.filter($"age" >30).show() println(personDF.filter($"age" >30).count()) //按照age分组统计个数 personDF.groupBy("age").count().show() //--------------------DSL风格语法--------------end //--------------------SQL风格语法--------------start personDF.createTempView("person") spark.sql("select * from person").show() spark.sql("select count(*) from person").show() spark.sql("select * from person where age >30").show() spark.sql("select * from person order by age desc").show() //--------------------SQL风格语法--------------end //hivesql ----->底层使用一个sql解析引擎,把hivesql语句翻译成mapreduce代码 //7、关闭 sc.stop() spark.stop() } }
8.2 通过StructType指定schema 实现将rdd转换成DataFrame
-
1、代码开发
package cn.itcast.sparksql import org.apache.spark.SparkContext import org.apache.spark.rdd.RDD import org.apache.spark.sql.types.{IntegerType, StringType, StructField, StructType} import org.apache.spark.sql.{DataFrame, Row, SparkSession} //todo:利用StructType指定schema 将rdd转换成dataFrame object SparksqlSchema { def main(args: Array[String]): Unit = { //1、构建SparkSession val spark: SparkSession = SparkSession .builder() .appName("SparksqlSchema") .master("local[2]") .getOrCreate() //2、构建SparkContext val sc: SparkContext = spark.sparkContext sc.setLogLevel("warn") //3、读取文件数据 val rdd1: RDD[Array[String]] = sc.textFile("E:\\person.txt").map(_.split(" ")) //4、将rdd1与Row对象进行关联 val rowRDD: RDD[Row] = rdd1.map(x =>Row(x(0).toInt,x(1),x(2).toInt)) //5、指定dataFrame的schema 在指定schema的时候当前字段的类型必要要跟Row对象的数据类型一致 val schema = StructType( StructField("id", IntegerType, true) :: StructField("name", StringType, false) :: StructField("age", IntegerType, false) :: Nil) val dataFrame: DataFrame = spark.createDataFrame(rowRDD,schema) //打印schema dataFrame.printSchema() dataFrame.show() dataFrame.createTempView("t_person") spark.sql("select * from t_person where age >25").show() sc.stop() spark.stop() } }
9、sparksql操作hivesql
-
1、引入依赖
<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-hive_2.11</artifactId> <version>2.1.3</version> </dependency>
-
2、代码开发
package cn.itcast.sparksql import org.apache.spark.sql.SparkSession //todo:利用sparksql操作hivesql object HiveSupport { def main(args: Array[String]): Unit = { //1、创建SparkSession val spark: SparkSession = SparkSession .builder() .appName("HiveSupport") .master("local[2]") .enableHiveSupport() //开启对hivesql的支持 .getOrCreate() //2、通过sparkSession操作hivesql //2.1 先创建一个hive表 //spark.sql("create table user(id string,name string,age int) row format delimited fields terminated by ','") //2.2 加载数据到hive表中 //spark.sql("load data local inpath './data/user.txt' into table user") //2.3 查询 spark.sql("select * from user").show() spark.stop() } }
10、使用sparksql操作关系型数据库
10.1 sparksql从mysql加载数据
-
1、代码开发
package cn.itcast.sparksql import java.util.Properties import org.apache.spark.sql.{DataFrame, SparkSession} //todo:sparksql从mysql表中加载数据生成dataFrame object DataFromMysql { def main(args: Array[String]): Unit = { //1、创建SparkSession val spark: SparkSession = SparkSession.builder().appName("DataFromMysql").master("local[2]").getOrCreate() //2、读取mysql表中数据 //2.1 定义url连接 val url="jdbc:mysql://node1:3306/spark" //2.2 定义表名 val table="iplocation" //2.3 定义属性 val properties=new Properties() properties.setProperty("user","root") properties.setProperty("password","123456") val mysqlDF: DataFrame = spark.read.jdbc(url,table,properties) mysqlDF.printSchema() mysqlDF.show() spark.stop() } }
10.2 sparksql把处理的结果写回到mysql表中
-
1、代码开发(本地运行)
package cn.itcast.sparksql import java.util.Properties import org.apache.spark.sql.{DataFrame, SparkSession} //todo:通过sparksql把结果数据写入到mysql表中 object Data2Mysql { def main(args: Array[String]): Unit = { //1、创建SparkSession val spark: SparkSession = SparkSession .builder() .appName("Data2Mysql") .master("local[2]") .getOrCreate() //2、读取mysql表中数据 //2.1 定义url连接 val url="jdbc:mysql://node1:3306/spark" //2.2 定义表名 val table="iplocation" //2.3 定义属性 val properties=new Properties() properties.setProperty("user","root") properties.setProperty("password","123456") val mysqlDF: DataFrame = spark.read.jdbc(url,table,properties) //把dataFrame注册成一张表 mysqlDF.createTempView("iplocation") //通过sparkSession调用sql方法 //需要统计经度和维度出现的人口总数大于1000的记录 保存到mysql表中 val result: DataFrame = spark.sql("select * from iplocation where total_count >1000") //保存结果数据到mysql表中 //mode:指定数据的插入模式 //overwrite: 表示覆盖,如果表不存在,事先帮我们创建 //append :表示追加, 如果表不存在,事先帮我们创建 //ignore :表示忽略,如果表事先存在,就不进行任何操作 //error :如果表事先存在就报错(默认选项) result.write.mode("ignore").jdbc(url,"itcast1",properties) //关闭 spark.stop() } }
-
2、代码开发(集群运行)
package cn.itcast.sparksql import java.util.Properties import org.apache.spark.sql.{DataFrame, SparkSession} //todo:通过sparksql把结果数据写入到mysql表中 object Data2Mysql { def main(args: Array[String]): Unit = { //1、创建SparkSession val spark: SparkSession = SparkSession .builder() .appName("Data2Mysql") .getOrCreate() //2、读取mysql表中数据 //2.1 定义url连接 val url="jdbc:mysql://node1:3306/spark" //2.2 定义表名 val table="iplocation" //2.3 定义属性 val properties=new Properties() properties.setProperty("user","root") properties.setProperty("password","123456") val mysqlDF: DataFrame = spark.read.jdbc(url,table,properties) //把dataFrame注册成一张表 mysqlDF.createTempView("iplocation") //通过sparkSession调用sql方法 //需要统计经度和维度出现的人口总数大于1000的记录 保存到mysql表中 val result: DataFrame = spark.sql("select * from iplocation where total_count >1000") //保存结果数据到mysql表中 //mode:指定数据的插入模式 //overwrite: 表示覆盖,如果表不存在,事先帮我们创建 //append :表示追加, 如果表不存在,事先帮我们创建 //ignore :表示忽略,如果表事先存在,就不进行任何操作 //error :如果表事先存在就报错(默认选项) result.write.mode(args(0)).jdbc(url,args(1),properties) //关闭 spark.stop() } }
-
1、提交任务
spark-submit \ --master spark://node1:7077 \ --class cn.itcast.sparksql.Data2Mysql \ --executor-memory 1g \ --total-executor-cores 4 \ --jars /export/servers/hive/lib/mysql-connector-java-5.1.35.jar \ --driver-class-path /export/servers/hive/lib/mysql-connector-java-5.1.35.jar \ original-spark_class15-1.0-SNAPSHOT.jar \ append \ itcast100
-