文章目录
SparkSQL
SparkSession
- SparkSession是SparkSQL的编程入口
//SparkSession通过SparkSession.builder()构建
val spark=SparkSession.builder()
.master("local[2]")
.appName("start")
.getOrCreate()
SparkSQL数据抽象
- SparkSQL的数据抽象有df和ds两种
DataFrames
- 产生版本:Spark1.3.0
DataFrames列已命名的Dataset。相当于关系型数据库的表,R/Python的data frame,但底层做了更丰富的优化。它可从结构化数据文件、hive表、其他数据库、RDD创建。在API层面,DataFrame表现为多个Row的Dataset,比如在scala API里,DataFrame 类型为 Dataset[Row],而在Java里则是Dataset<Row>。
DataFrames的创建
使用SparkSession,应用程序可从已知的RDD、Hive表、其他spark数据源如(文本文件,json文件等)创建。
// 通过SparkSession读取json文件,创建DF对象
val df1=spark.read.format("json").load("amos/src/main/resources/sparksql/employees.json")
// 通过SparkSession读取文本文件,创建DF对象
val df2=spark.read.text("amos/src/main/resources/sparksql/people.txt")
// 把这个DataFrame 注册为一个 SQL 临时视图
df1.createOrReplaceTempView("t1")
spark.sql("select * from t1").show(5)
/*
+-------+------+
|Michael| 3000|
| Andy| 4500|
| Justin| 3500|
| Berta| 4000|
+-------+------+
*/
DataFrames的DSL操作
DataFrame操作,又叫无类型的Dataset操作
//导入$符号的使用
import spark.implicits._
//查询单列
df1.select("name")
// 查询每一条记录, 但 工资 乘 10
df1.select($"name", $"salary" * 10)
//条件查询
df1.select("name").where($"salary">=4000)
.show()
//另一种写法
//df1.filter($"salary">=4000).show()
/*
最后一次的输出:
| name|
+-----+
| Andy|
|Berta|
+-----+
+-----+------+
| name|salary|
+-----+------+
| Andy| 4500|
|Berta| 4000|
+-----+------+
*/
DataSets
- 产生版本:Spark1.6.0
Datasets分布式数据集。提供了RDD的优点(强类型、强大的lambda函数)和Spark-SQL优化了的执行引擎。它可由JVM对象创建,然后使用函数式转换进行修改,比如map、flatmap、filter等。
三种数据抽象的使用
三种数据抽象的概念和区别
- RDD
1.RDD[T] T类型规定了RDD中存放的数据类型
- DataFrames
2.DataFrame=Dataset[Row]
val df1: DataFrame = spark.read.json("\\src\\main\\resources\\sparksql\\people.json")
// df本质上是ds的一个特例,即Row作为泛型的ds
// type DataFrame = Dataset[Row]
// Row是一个特质(接口)
// trait Row extends Serializable {
// Row的一个实现类是GenericRow
// class GenericRow(val values: Array[Any]) extends Row {
// 由于GenericRow不能完全表示数据的字段名和类型,所以使用GenericRowWithSchema
// class GenericRowWithSchema(
// values: Array[Any], override val schema: StructType)
// extends GenericRow(values)
//
// case class StructType(val fields: Array[StructField])
// case class StructField(
// name: String, 字段名
// dataType: DataType 字段类型
// )
// GenericRow有一个成员属性 values : Array[Any] 用来存储表每一行的内容数据
// StructType有一个成员属性fields: Array[StructField]
// Array[StructField](StructField("name",StringType),StructField("age",IntegerType))
- DataSets
// 3.Dataset[T] 与RDD类似,通常使用样例类作为DS的泛型
// Dataset直接通过样例类的属性 映射表的字段名和类型
// case class Person(name:String,age:Int)
// Dataset[Person]
汇总
Dataset是对RDD在sql特性上的封装,RDD不能执行sql查询,而DataSet可以。
DataFrame是Dataset的特例,DataFrame = Dataset[Row],DataFrame支持所有Dataset操作,DataFrame每一行是固定Row的类型,只能通过索引或列名访问列值,而Dataset每一行都是明确的类型,访问比较方便。如果行的类型不确定时,使用DataFrame比较合适。
三种数据抽象的相互转换
- RDD转换
// 1. RDD转DF
// 1.1 需要导入sparkSession中的隐式转换
import spark.implicits._
// 1.2 将RDD转换成元组的泛型 (String,Int)
val rdd = sc.textFile("amos/src/main/resources/sparksql/people.txt")
val rdd1 = rdd.map(x => {
val str = x.split(",")
(str(0), str(1).trim.toInt)
})
// 1.3 rdd.toDF("第一列列名","第一列列名"...)
val df = rdd1.toDF("name", "age")
// 2. RDD转DS
// 2.1 导入隐式转换
// 2.2 提前定义一个样例类C,样例类C的属性与字段对应
// 2.3 将RDD转换成刚才定义的C泛型
// 2.4 rdd:RDD[C] .toDS => Dataset[C]
val rdd2 = rdd1.map(x => people(x._1, x._2))
val ds=rdd2.toDS()
- DS、DF相互转化
// 3. DF转DS
// 3.1 注意df和as[C]的C这个样例类,字段名和字段类型必须一致
val ds1=df.as[people]
// 4. DS转DF
// DS转DF时会自动根据样例类的属性来映射字段名和字段类型
val df1=ds.toDF()
- DS、DF转换RDD
// 5. DS和DF转RDD
// 直接ds.rdd 或者df.rdd
// df转成的RDD泛型为Row
val rdd3=ds.rdd
val rdd4=df.rdd
// 从row对象中取出字段值时
// 由于这些字段值使用Array[Any]进行存储
// 所以取出时需要进行类型转换才能使用
// 如果类型转换编写错误,运行时会抛出异常
// 但是在编译时期无法发现错误,所以DF也被称为类型不安全
rdd4.map(x=>{
(x.get(0),x.getInt(1))
})
三种数据抽象的构建
// 3.1 rdd通过sc创建
// 3.2 ds 需要提前声明样例类,之后通过已有的df或者rdd进行转换
// 3.3 df的创建可以使用SparkSession提供的现有方法,或者自己构造
// 3.3.1 通过SparkSession创建DF
// 语法 sparkSession.read.format("数据源类型").load("文件路径")
SparkSQL读写
读取文件
- read加载数据
// 语法:spark.read.text(文件路径)
// 支持的文件有:csv jdbc json orc parquet text等
val df2 = spark.read.text("amos/src/main/resources/sparksql/people.txt")
注意:加载数据的相关参数需写到上述方法中。如:textFile需传入加载数据的路径,jdbc需传入JDBC相关参数。
- format指定加载数据类型
// spark.read.format("文件类型")[.option("…")].load("文件路径")
val df1 = spark.read.format("json").load("amos/src/main/resources/sparksql/employees.json")
- 参数详解
- format("…"):指定加载的数据类型,包括"csv"、“jdbc”、“json”、“orc”、“parquet"和"textFile”。
- load("…"):在"csv"、“orc”、“parquet"和"textFile"格式下需要传入加载数据的路径。
- option(”…"):在"jdbc"格式下需要传入JDBC相应参数,url、user、password和 dbtable。
写出文件
- write保存数据
语法:spark.write
val df=spark.read.text("C:\\Users\\hushaonan\\Desktop\\logs\\access.log-20190929")
df.write.text("C:\\Users\\hushaonan\\Desktop\\logs\\output\\test.txt")
注意:保存数据的相关参数需写到上述方法中。如:textFile需传入加载数据的路径,jdbc需传入JDBC相关参数。
- format指定文件类型
//df.write.format("…")[.option("…")].save("…")
df.write.format("json").save("C:\\Users\\hushaonan\\Desktop\\output\\test")
df.write.format("parquet").save("C:\\Users\\hushaonan\\Desktop\\output\\test1")
df.write.format("csv").save("C:\\Users\\hushaonan\\Desktop\\output\\test2")
-
参数详解:
- format("…"):指定保存的数据类型,包括"csv"、“jdbc”、“json”、“orc”、“parquet"和"textFile”。
- save ("…"):在"csv"、“orc”、“parquet"和"textFile"格式下需要传入保存数据的路径。
- option(”…"):在"jdbc"格式下需要传入JDBC相应参数,url、user、password和dbtable
-
文件保存选项
可以采用SaveMode执行存储操作,SaveMode定义了对数据的处理模式。SaveMode是一个枚举类,其中的常量包括:
- Append:当保存路径或者表已存在时,追加内容;
- Overwrite: 当保存路径或者表已存在时,覆写内容;
- ErrorIfExists:当保存路径或者表已存在时,报错;
- Ignore:当保存路径或者表已存在时,忽略当前的保存操作。
//df.write.mode(SaveMode.Append).save("… …")
df.write.mode(saveMode="append").save("C:\\Users\\hushaonan\\Desktop\\logs\\output\\test.txt")
this.mode = saveMode.toLowerCase match {
case "overwrite" => SaveMode.Overwrite
case "append" => SaveMode.Append
case "ignore" => SaveMode.Ignore
case "error" | "default" => SaveMode.ErrorIfExists
case _ => throw new IllegalArgumentException(s"Unknown save mode: $saveMode. " +
"Accepted save modes are 'overwrite', 'append', 'ignore', 'error'.")
}