SparkSQL
文章目录
1、简介
DataFrame
- 在 Spark 中,DataFrame 是一种以 RDD 为基础的分布式数据集,类似于传统数据库中的二维表格。DataFrame 与 RDD 的主要区别在于,前者带有 schema 元信息,即 DataFrame 所表示的二维表数据集的每一列都带有名称和类型。这使得 Spark SQL 得以洞察更多的结构 信息,从而对藏于 DataFrame 背后的数据源以及作用于 DataFrame 之上的变换进行了针对性的优化,最终达到大幅提升运行时效率的目标。反观 RDD,由于无从得知所存数据元素的具体内部结构,Spark Core 只能在 stage 层面进行简单、通用的流水线优化。
- 同时,与 Hive 类似,DataFrame 也支持嵌套数据类型(struct、array 和 map)。从 API 易用性的角度上看,DataFrame API 提供的是一套高层的关系操作,比函数式的 RDD API 要 更加友好,门槛更低。
DataSet
- DataSet 是分布式数据集合。DataSet 是 Spark 1.6 中添加的一个新抽象,是 DataFrame 的一个扩展。它提供了 RDD 的优势(强类型,使用强大的 lambda 函数的能力)以及 Spark SQL 优化执行引擎的优点。DataSet 也可以使用功能性的转换(操作 map,flatMap,filter 等等)。
- DataSet 是 DataFrame API 的一个扩展,是 SparkSQL 最新的数据抽象
- 用户友好的 API 风格,既具有类型安全检查也具有 DataFrame 的查询优化特性;
- 用样例类来对 DataSet 中定义数据的结构信息,样例类中每个属性的名称直接映射到 DataSet 中的字段名称;
- DataSet 是强类型的。比如可以有 DataSet[Car],DataSet[Person]。
- DataFrame 是 DataSet 的特列,DataFrame=DataSet[Row] ,所以可以通过 as 方法将 DataFrame 转换为DataSet。Row 是一个类型,跟 Car、Person 这些的类型一样,所有的表结构信息都用 Row 来表示。获取数据时需要指定顺序
2、Shell命令
DataFrame
-
创建 DataFrame
# 支持 csv format jdbc json load option options orc parquet schema table text textFile 多种创建 spark.read.[csv format jdbc json load option options orc parquet schema table text textFile] # 事例(json的方式创建,json文件需要保证每行都是json格式) val df = spark.read.json("data/user.json")
-
SQL语法
# 查询 spark.sql("select查询语句") #事例 # 1、创建 DataFrame val df = spark.read.json("data/user.json") # 2、创建临时表 user df.createOrReplaceTempView("user") # 3、查询 spark.sql("SELECT * FROM user")
-
DSL语法(调用方法)
#事例 # 1、创建 DataFrame val df = spark.read.json("data/user.json") # 2、查看"username"列数据 df.select("name").show() # 2、查看"username"列数据以及"age+1"数据 # 注意:涉及到运算的时候, 每列都必须使用$, 或者采用引号表达式:单引号+字段名 df.select($"username",$"age" + 1).show df.select('username, 'age + 1).show()
-
RDD <-> DataFrame(相互转换)
# RDD 转换为 DataFrame(.toDF) rdd.toDF(字段名) rdd.toDF("id", "name") # DataFrame 转换为 RDD(.rdd) df.rdd
DataSet
-
创建DataSet
# 1、创建类 case class Person(name: String, age: Long) # 2、创建DataSet(.toDS) Seq(Person("fff", 20), Person("zzz", 30)).toDS()
-
RDD <-> DataSet(相互转换)
case class User(name:String, age:Int) # RDD 转换为 DataSet(.toDS) rdd.map{data => User(data._1, data._2)}.toDS # DataSet 转换为 RDD(.rdd) ds.rdd
-
DataFrame <-> DataSet(相互转换)
case class User(name:String, age:Int) # DataFrame 转换为 DataSet(.as[]) df.as[User] # DataSet 转换为 DataFrame (.rdd) ds.toDF
3、IDEA开发
环境准备
-
添加依赖(pom.xml)
<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.12</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-yarn_2.12</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.12</artifactId> <version>3.0.0</version> </dependency>
RDD、DataFrame、DataSet 三者相互转换
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
object Spark01_SQL_Basic {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSession
//创建Spark配置对象
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
//创建SparkSession对象
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
import spark.implicits._
//TODO 业务逻辑
//创建DataFrame(json)
val df: DataFrame = spark.read.json("datas/user.json")
//DataFrame => SQL语法
df.createOrReplaceTempView("user")
spark.sql("select * from user").show()
//DataFrame => DSL语法
df.select("name", "age").show()
df.select($"age" + 1).show()
df.select('age + 1).show()
//创建DataSet
val ds: Dataset[Int] = Seq(1, 2, 3, 4).toDS()
ds.show()
//RDD <=> DataFrame(相互转换)
val rdd: RDD[(Int, String, Int)] = spark.sparkContext.makeRDD(List((1, "fff", 20)))
val df1: DataFrame = rdd.toDF("id", "name", "age")
val rdd1: RDD[Row] = df1.rdd
//DataFrame <=> DataSet(相互转换)
val ds2: Dataset[User] = df1.as[User]
val df2: DataFrame = ds2.toDF()
//RDD <=> DataSet(相互转换)
val ds3: Dataset[User] = rdd.map(data => User(data._1, data._2, data._3)).toDS()
val rdd3: RDD[User] = ds3.rdd
//TODO 关闭连接
spark.stop()
}
//创建User类
case class User(id: Int, name: String, age: Int)
}
自定义函数
UDF
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
object Spark02_SQL_UDF {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSession
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
import spark.implicits._
//TODO 业务逻辑
//DataFrame
val df: DataFrame = spark.read.json("datas/user.json")
df.createOrReplaceTempView("user")
//自定义udf,第一个参数:名称,第二个参数:逻辑方法
spark.udf.register("prifix", (name: String) => "Name: " + name)
spark.sql("select prifix(name), age from user").show()
//TODO 关闭连接
spark.stop()
}
}
UDAF
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.Aggregator
import org.apache.spark.sql.{DataFrame, Encoder, Encoders, SparkSession, functions}
object Spark03_SQL_UDAF {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSession
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
//TODO 业务逻辑
val df: DataFrame = spark.read.json("datas/user.json")
df.createOrReplaceTempView("user")
//自定义UDAF
spark.udf.register("ageAvg", functions.udaf(new MyUDAF))
spark.sql("select ageAvg(age) from user").show()
//TODO 关闭连接
spark.stop()
}
//创建Buff类
case class Buff(var total: Long, var count: Long)
//自定义UDAF类
class MyUDAF extends Aggregator[Long, Buff, Long]{
//buff初始数据
override def zero: Buff = Buff(0L, 0L)
//缓冲区中的业务逻辑
override def reduce(b: Buff, a: Long): Buff = {
b.total += a
b.count += 1
b
}
//将缓冲区间合并的业务逻辑
override def merge(b1: Buff, b2: Buff): Buff = {
b1.total += b2.total
b1.count += b2.count
b1
}
//最终业务逻辑
override def finish(reduction: Buff): Long = reduction.total / reduction.count
//buff编码类型(Encoders.product:自定义类型)
override def bufferEncoder: Encoder[Buff] = Encoders.product
//输出编码类型(Encoders.scalaLong:系统自带类型)
override def outputEncoder: Encoder[Long] = Encoders.scalaLong
}
}
4、加载和保存方式
加载数据
# 支持 csv format jdbc json load option options orc parquet schema table text textFile 多种加载方式
spark.read.[csv format jdbc json load option options orc parquet schema table text textFile]
# json
spark.read.json("data/user.json")
# load
# format("…"):指定加载的数据类型,包括"csv"、"jdbc"、"json"、"orc"、"parquet"和"textFile"
# option("…"):在"jdbc"格式下需要传入 JDBC 相应参数,url、user、password 和 dbtable
# load("…"):在"csv"、"jdbc"、"json"、"orc"、"parquet"和"textFile"格式下需要传入加载数据的路径
spark.read.format("…")[.option("…")].load("…")
# load -> json
spark.read.format("json").load("data/user.json")
# load -> jdbc
spark.read.format("jdbc")
.option("url", "jdbc:mysql://localhost:3306/test")
.option("user", "root")
.option("password", "123456")
.option("dbtable", "test")
.load()
保存数据
# 支持 csv jdbc json orc parquet textFile… … 保存数据方式
df.write.[csv jdbc json orc parquet textFile… …]
# 事例
df.write.save("路径")
# save
# format("…"):指定保存的数据类型,包括"csv"、"jdbc"、"json"、"orc"、"parquet"和"textFile"
# option("…"):在"jdbc"格式下需要传入 JDBC 相应参数,url、user、password 和 dbtable
# mode("…"):数据保存类型,"error"(default,抛异常)、"append"(追加)、"overwrite"(覆盖)、"ignore"(忽略)
# save ("…"):在"csv"、"orc"、"parquet"和"textFile"格式下需要传入保存数据的路径
df.write.format("…")[.option("…")][.mode("…")].save("…")
#save -> json
spark.write.format("json").save("data/user.json")
# save -> jdbc
df.write.format("jdbc")
.option("url", "jdbc:mysql://localhost:3306/test")
.option("user", "root")
.option("password", "123456")
.option("dbtable", "test1")
.mode(SaveMode.Append)
.save()
API
MySQL
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.Aggregator
import org.apache.spark.sql._
object Spark04_SQL_JDBC {
def main(args: Array[String]): Unit = {
//TODO 创建SparkSession
val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
//TODO 业务逻辑
val df: DataFrame = spark.read.format("jdbc")
.option("url", "jdbc:mysql://localhost:3306/test")
.option("user", "root")
.option("password", "123456")
.option("dbtable", "test")
.load()
df.show()
df.write.format("jdbc")
.option("url", "jdbc:mysql://localhost:3306/test")
.option("user", "root")
.option("password", "123456")
.option("dbtable", "test1")
.mode(SaveMode.Append)
.save()
//TODO 关闭连接
spark.stop()
}
}
Hive
-
导入依赖(pom.xml)
<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.12</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-yarn_2.12</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.12</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.27</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-hive_2.12</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.2.1</version> </dependency>
-
程序
import org.apache.spark.SparkConf import org.apache.spark.sql._ object Spark05_SQL_Hive { def main(args: Array[String]): Unit = { //TODO 创建SparkSession val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL") //开启hive支持(enableHiveSupport) val spark: SparkSession = SparkSession.builder().enableHiveSupport().config(sparkConf).getOrCreate() //TODO 业务逻辑 spark.sql("show tables").show() //TODO 关闭连接 spark.stop() } }