帅气的目录
0、环境准备
依赖
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-core -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>3.0.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-sql -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.12</artifactId>
<version>3.0.2</version>
</dependency>
代码壳
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{SparkSession, DataFrame, Dataset, Row}
object Hello {
def main(args: Array[String]): Unit = {
//创建SparkContext对象
val c0: SparkConf = new SparkConf().setAppName("a0").setMaster("local[*]")
val sc: SparkContext = new SparkContext(c0)
//创建SparkSession对象
val c1: SparkConf = new SparkConf().setAppName("a1").setMaster("local[*]")
val spark: SparkSession = SparkSession.builder().config(c1).getOrCreate()
//隐式转换支持
import spark.implicits._
}
}
1、SparkSQL简介
- SparkSQL:Spark的结构化数据处理模块
无缝地整合了SQL+Spark编程
主要内容:DataFrame和DataSet
1.1、DataFrame
- 一种以RDD为基础的分布式数据集,类似于二维表格
- 和RDD相比,多了元数据
val s: Seq[(String, Int)] = Seq(("a", 2), ("b", 3))
val df: DataFrame = s.toDF("name", "age")
df.show()
/*
+----+---+
|name|age|
+----+---+
| a| 2|
| b| 3|
+----+---+
*/
- 源码是
Dataset[Row](表明DataFrame是DataSet的特例)
val df: Dataset[Row] = Seq(("a", 2), ("b", 3)).toDF
/*
+---+---+
| _1| _2|
+---+---+
| a| 2|
| b| 3|
+---+---+
*/
1.2、DataSet
- 比DataFrame更具体
- 用样例类来定义DataSet中数据的结构信息,样例类中每个属性的名称直接映射到字段名称
import org.apache.spark.sql.{SparkSession, Dataset}
object Hello {
def main(args: Array[String]): Unit = {
// 创建SparkSession对象 并 设置隐式转换
val spark: SparkSession = SparkSession.builder()
.master("local[*]").appName("a").getOrCreate()
import spark.implicits._
// 创建DataSet
val ds: Dataset[Hero] = Seq(Hero("剑圣", 9), Hero("先知", 7)).toDS
ds.show()
}
}
case class Hero(name: String, level: Int)
/*
+----+-----+
|name|level|
+----+-----+
|剑圣| 9|
|先知| 7|
+----+-----+
*/
2、RDD、DataFrame、DataSet 互转
- 创建SparkSession对象后,写上
import spark.implicits._来支持隐式转换
注意:spark不是包名,而是SparkSession对象的名称,implicits是spark内部的对象

- 创建RDD
val r: RDD[(String, Int)] = sc.makeRDD(Seq(("a", 3), ("b", 6)))
- RDD转DataFrame
val df: DataFrame = r.toDF("name", "age")
- RDD转DataSet
case class Student(name: String, age: Int)
val ds: Dataset[Student] = r.map(t => Student(t._1, t._2)).toDS()
- DataFrame转DataSet
case class Student(name: String, age: Int)
val ds: Dataset[Student] = df.as[Student]
- DataFrame转RDD
val df2r:RDD[Row] = df.rdd
- DataSet转DataFrame
val ds2df: DataFrame = ds.toDF
- DataSet转RDD
val ds2r: RDD[Student] = ds.rdd
3、用户自定义聚合函数(UDAF)
- UDAF全称:User Defined Aggregate Function
- 俗称:多进一出
3.1、Aggregator
- 自定义聚合器的基类,可在Dataset中使用
- 功能:将组里所有元素归约(reduce)为一个值
- 参数
IN:用于聚合的输入类型
BUF:用于归约(reduce)的中介值
OUT:输出结果类型 - 源码如下(本人稍微翻译),在
org.apache.spark.sql.expressions
import org.apache.spark.sql.{Encoder, TypedColumn}
import org.apache.spark.sql.catalyst.encoders.encoderFor
import org.apache.spark.sql.catalyst.expressions.aggregate.{AggregateExpression, Complete}
import org.apache.spark.sql.execution.aggregate.TypedAggregateExpression
abstract class Aggregator[-IN, BUF, OUT] extends Serializable {
// 用于聚合的初始值
def zero: BUF
//归约(合并2个值为1个值):不断输入数据(IN)来迭代中间值(BUF)
def reduce(b: BUF, a: IN): BUF
// 合并两个中间值
def merge(b1: BUF, b2: BUF): BUF
// 中间值转换成最终值
def finish(reduction: BUF): OUT
// Specifies the `Encoder` for the intermediate value type.
def bufferEncoder: Encoder[BUF]
// 指定 用于最终输出值类型 的 编码器
def outputEncoder: Encoder[OUT]
// 将该`Aggregator`作为`TypedColumn`返回,可用于`Dataset`操作
def toColumn: TypedColumn[IN, OUT] = {
implicit val bEncoder = bufferEncoder
implicit val cEncoder = outputEncoder
val expr =
AggregateExpression(
TypedAggregateExpression(this),
Complete,
isDistinct = false)
new TypedColumn[IN, OUT](expr, encoderFor[OUT])
}
}
3.2、代码示例(求平均值)
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
object Hello {
def main(args: Array[String]): Unit = {
// 创建SparkContext对象
val c0: SparkConf = new SparkConf().setAppName("a0").setMaster("local[*]")
val sc: SparkContext = new SparkContext(c0)
// 创建SparkSession对象
val c1: SparkConf = new SparkConf().setAppName("a1").setMaster("local[*]")
val spark: SparkSession = SparkSession.builder()
.master("local[*]")
.appName("c1")
.config(c1)
.getOrCreate()
// 隐式转换支持
import spark.implicits._
// 创建RDD
val r: RDD[(String, Int)] = sc.makeRDD(Seq(("a", 3), ("b", 6), ("c", 6)))
val ds: Dataset[User] = r.map(t => User(t._1, t._2)).toDS()
// 创建临时视图
ds.createOrReplaceTempView("student")
// 计算平均年龄(内置方法)
spark.sql("SELECT AVG(age) FROM student").show
// 计算平均年龄(自定义方法)
// 创建自定义函数对象
val myAvg = new A
// 自定义函数对象转为查询列
val col = myAvg.toColumn
// 查询列
ds.select(col).show()
}
}
// 输入类型的样例类
case class User(name: String, age: Int)
// 缓存数据类型的样例类
case class AgeBuffer(var sum: Long, var num: Int)
// 用户自定义聚合函数
import org.apache.spark.sql.expressions.Aggregator
import org.apache.spark.sql.{Encoders, Encoder}
class A extends Aggregator[User, AgeBuffer, Double] {
// 缓存数据的初始值
override def zero: AgeBuffer = AgeBuffer(0L, 0)
// 分区内数据聚合
override def reduce(b: AgeBuffer, a: User): AgeBuffer = {
b.sum += a.age
b.num += 1
b
}
// 分区间数据合并
override def merge(b1: AgeBuffer, b2: AgeBuffer): AgeBuffer = {
b1.sum += b2.sum
b1.num += b2.num
b1
}
// 最终结果
override def finish(reduction: AgeBuffer): Double = {
reduction.sum / reduction.num.toDouble
}
// 中间值编码器,用于序列化
override def bufferEncoder: Encoder[AgeBuffer] = {
Encoders.product // 自定义类的固定写法
}
// 输出值解码器,用于序列化
override def outputEncoder: Encoder[Double] = {
Encoders.scalaDouble
}
}
4、数据读写
可以Xshell开2个窗口来玩
读数据:spark.read.format("类型").load("路径")
写数据:df.writer.format("类型").save("路径")
// 创建DataFrame
val df0 = sc.makeRDD(Seq(("a", 3), ("b", 6)), 2).toDF("name", "age")
// 数据保存
df0.write.json("b.json")
// 数据读取
val df1 = spark.read.json("b.json")
df1.show()
5、视图的使用
createTempView:创建临时视图
// 创建DataFrame
val s = Seq(("BM", "物理", 8), ("DK", "物理", 9), ("DH", "物理", 7), ("MK", "物理", 8),
("BM", "化学", 6), ("DK", "化学", 5), ("DH", "化学", 3), ("MK", "化学", 4))
val df = sc.makeRDD(s).toDF("name", "course", "score")
/* 需求:各科成绩前两名 */
// 创建临时视图0
df.createTempView("scores")
// 创建临时视图1
spark.sql(
"""
|SELECT
| *,
| RANK() OVER(PARTITION BY course ORDER BY score DESC) AS r
|FROM scores;
|""".stripMargin).createTempView("temp1")
// 创建临时视图2(可以用视图1的表名)
spark.sql("SELECT * FROM temp1 WHERE r<3").createTempView("temp2")
// 打印
spark.sql("SELECT * FROM temp2").show()
6、连接已有的HIVE
- 拷贝MySQL驱动到Spark
cp $HIVE_HOME/lib/mysql-connector-java-5.1.27-bin.jar $SPARK_HOME/jars/

- 在Spark创建
hive-site.xml,内容从HIVE的hive-site.xml那里拷贝过来
vim $SPARK_HOME/conf/hive-site.xml
<configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://hadoop102:3306/metastore?useSSL=false</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>root</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>123456</value>
</property>
<property>
<name>hive.metastore.schema.verification</name>
<value>false</value>
</property>
</configuration>
- 测试
spark-sql --master yarn
show databases;
482



被折叠的 条评论
为什么被折叠?



