Spark SQL介绍
Spark SQL是用于处理结构化数据的Spark组件, 结构化数据来源可以是hive, json, parquet, 或者其他RDD增加schema得到
特点
- 易整合, spark SQL可以与spark程序混合编程
- 统一的数据访问方式, 对原始数据提供一套统一的访问方式, 例如: 访问Hive,Avro, ORC, JSON等
- 兼容hive, 可以使用hive的HQL语句
- 标准的数据连接, 可以连接JDBC/ODBC
- 支持2种编程API, SQL方式, DSL方式(即使用DataFrame调用API)
DataFrame
DataFrame是一种按列组织的分布式数据集
DataFrame和RDD的区别:
- DataFrame包含了表的描述信息(schema), 多少列, 列名, 类型, 能否为空
- 是一种特殊的RDD, 可以理解为: RDD+ schema组成了DataFrame
- 主要区别就是: DataFrame带有schema元数据信息, RDD不知道数据集内部的结构. spark执行作业的时候, 可以根据数据结构信息进行计算优化, 提高运行效率
编写SparkSQL查询
配置maven的pom.xml
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
SparkSession的3种创建方式
// 方式1:
val session1 = SparkSession.builder().appName("").master("local").getOrCreate()
// 方式2:
val conf = new SparkConf().setAppName("").setMaster("local")
val session2 = SparkSession.builder.config(conf).getOrCreate
// 方式3: 可添加hive支持
val session3 = SparkSession.builder().appName("").master("local").enableHiveSupport.getOrCreate()
// 使用完成后, 注意释放资源
session.close()
RDD转换为DataFrame的3种方式
- 手动创建
val lineRDD:RDD = sparkContext.parallize(List(("Jim", 18),("Lily", 18)))
import sparkSession.implicits._
val dataFrame:DataFrame = lineRDD.toDF("name","age")
- 通过反射创建
val personRDD:RDD[Person] = xxx
import sparkSession.implicits._
val dataFrame:DataFrame = lineRDD.toDF // 无需指定字段
- 手动指定row和field创建
val personRDD:RDD[Row] = xxx
val schema: StructType = StructType(List(
StructField("id", LongType, true),
StructField("name", StringType, true),
StructField("age", IntegerType, true),
StructField("fv", DoubleType, true)
))
//将RowRDD关联schema
val dataFrame: DataFrame = sparkSession.createDataFrame(rowRDD, schema)
Spark SQL使用
- SQL方式
// 需要创建一个临时table后, 才能执行sql语句
bdf.registerTempTable("t_person")
//书写SQL(SQL方法应其实是Transformation)
val result: DataFrame = sparkSession.sql("SELECT * FROM t_person ORDER BY fv desc, age asc")
- DSL方式
// 无需创建临时表
val df1: DataFrame = bdf.select("name", "age", "fv")
import sqlContext.implicits._
val df2: DataFrame = df1.orderBy($"fv" desc, $"age" asc)
DataFrame转换为RDD
// 调用方法即可
val rdd:RDD[Row] = dataFrame.rdd
Dataset
从spark1.6 新加入的数据接口
使用Dataset可以方便的对字段数据进行访问, 例如: x.name, 然而在DataFrame中, 需要使用$"name"来访问, 可维护性更高
数据集的相互转换
// Dataset转RDD
val rdd:RDD[Person] = dataSet.rdd
// Dataset转DataFrame
val dataFrame = dataSet.toDF
// DataFrame转Dataset
val dataSet:DataSet[Person] = dataFrame.as[Person]
数据操作方法
DSL风格
ds.select(_.name)
ds.filter(_.age > 20)
SQL风格
sparkSession.sql("select * from table")
// session范围表, session可访问
df.createOrReplaceTempView("Person")
// 全局表, 整个应用可访问
df.createGlobalTempView("Person")
//注意, 访问全局表需要加全路径
val sql = "select * from global_temp.Person"
SPARK数据集的发展
RDD(2011) --> DataFrame(2013) --> Dataset(2015)
Dataset包含了RDD和DataFrame的功能
RDD, DataFrame, Dataset的区别
共同点:
都是spark中弹性分布式数据集
都具有transform算子和action算子, RDD具备的功能, DataFrame和Dataset基本都有
区别:
DataFrame与RDD相比, 多了schema(元数据), 数据是按列的方式存储, 类似于DB的一张表. 存储的是结构化的数据
支持更高级的API, RDD不能执行sparkSQL操作, DataFrame可以执行sparkSQL操作(SQL, DSL)
在spark2.0中, Dataset与DataFrame合并, Dataset分为强类型API和弱类型API, 强类型指的是Dataset保存的是手动创建的对象, 如:Person; 弱类型指的是保存的是Row; 具体体现在: DataFrame取特定字段, 只能通过Row.get(0)获取, 而Dataset可以使用操作对象属性的的方式获取, 这样可以在编译期间发现一些数据类型错误的问题, 并且提高编码效率
WordCount案例, SparkSQL版
"select value,count(1) from t groupby value"
各类数据源读取
// 读取JDBC数据源
val logs: DataFrame = spark.read.format("jdbc").options(
Map("url" -> "jdbc:mysql://localhost:3306/bigdata",
"driver" -> "com.mysql.jdbc.Driver",
"dbtable" -> "logs",
"user" -> "root",
"password" -> "123568")
).load()
// 读取json
val jsons: DataFrame = spark.read.json("/Users/Desktop/json")
// 读取csv
val csv: DataFrame = spark.read.csv("c://data//t.scv")
// 读取parquet
val parquetLine: DataFrame = spark.read.parquet("c://data//t.parquet")
// 读取mysql, 写入mysql
// 封装请求MySQL的配置信息
val prop = new Properties()
prop.put("user", "root")
prop.put("password", "root")
prop.put("driver", "com.mysql.jdbc.Driver")
// 把数据写入MySQL
personDF.write.mode("append").jdbc("jdbc:mysql://node03:3306/bigdata", "person", prop)