1 DataFrame编程
代码中创建DataFrame
//1.先创建SparkSession
val spark = SparkSession.builder()
.appName("CreateDF").master("local[2]")
.getOrCreate()
//2.通过SparkSession创建DF
val df = spark.read.json("D:/users.json")
//3.对DF做操作(sql)
//3.1 创建临时表
df.createOrReplaceTempView("user")
//3.2 查询临时表
spark.sql("select * from user").show
//4.关闭SparkSession
spark.stop()
1.1 DF转RDD
val rdd = df.rdd
2 使用DataSet进行编程
val spark = SparkSession.builder()
.appName("CreateDF").master("local[2]")
.getOrCreate()
import spark.implicits._
val list = List(User(10, "zs"), User(20, "lisi"), User(15, "ww"))
val ds = list.toDS()
// ds.show()
//在ds做sql查询
ds.createOrReplaceTempView("user")
spark.sql("select * from user where age>15").show
spark.stop()
2.1 三者的区别
RDD
- RDD一般和spark mlib同时使用
- RDD不支持sparksql操作
DataFrame
- 与RDD和Dataset不同,DataFrame每一行的类型固定为Row,每一列的值没法直接访问,只有通过解析才能获取各个字段的值。
- DataFrame与DataSet一般不与 spark mlib 同时使用
- DataFrame与DataSet均支持 SparkSQL 的操作,比如select,groupby之类,还能注册临时表/视窗,进行 sql 语句操作
- DataFrame与DataSet支持一些特别方便的保存方式,比如保存成csv,可以带上表头,这样每一列的字段名一目了然。
DataSet - Dataset和DataFrame拥有完全相同的成员函数,区别只是每一行的数据类型不同。 DataFrame其实就是DataSet的一个特例。
- DataFrame也可以叫Dataset[Row],每一行的类型是Row,不解析,每一行究竟有哪些字段,各个字段又是什么类型都无从得知,只能用上面提到的getAS方法或者共性中的第七条提到的模式匹配拿出特定字段。而Dataset中,每一行是什么类型是不一定的,在自定义了case class之后可以很自由的获得每一行的信息。
2.2 三者的共性
RDD、DataFrame、Dataset全都是 Spark 平台下的分布式弹性数据集,为处理超大型数据提供便利。
三者都有惰性机制,在进行创建、转换,如map方法时,不会立即执行,只有在遇到Action如foreach时,三者才会开始遍历运算。
三者都会根据 Spark 的内存情况自动缓存运算,这样即使数据量很大,也不用担心会内存溢出。
三者都有partition的概念
三者有许多共同的函数,如map, filter,排序等
在对 DataFrame和Dataset进行操作许多操作都需要这个包进行支持 import spark.implicits._ (spark只是为对象名 也可以是其他的名称 )
DataFrame和Dataset均可使用模式匹配获取各个字段的值和类型。
3 自定义聚合函数(弱类型的聚合函数 重点)
案例:对users中的年龄求和
object UDAFDemo {
def main(args: Array[String]): Unit = {
//在sql中,聚合函数如何使用
val spark = SparkSession
.builder()
.appName("UDAFDemo")
.master("local[*]")
.getOrCreate()
import spark.implicits._
val df = spark.read.json("D:/users.json")
df.createOrReplaceTempView("user")
//注册聚合函数
spark.udf.register("mySum",new MySum)
spark.sql("select mySum(age) from user ").show
spark.close()
}
}
//定义mySum
class MySum extends UserDefinedAggregateFunction {
//用来定义输入的数据类型
override def inputSchema: StructType = StructType(StructField("ele",DoubleType)::Nil)
//缓冲区的类型
override def bufferSchema: StructType = StructType(StructField("ele",DoubleType)::Nil)
//最终聚合结果的类型
override def dataType: DataType = DoubleType
//相同的输入是否返回相同的输出
override def deterministic: Boolean = true
//对缓冲区初始化
override def initialize(buffer: MutableAggregationBuffer): Unit = {
//在缓冲集合中初始化和
buffer(0)=0D
}
//分区内聚合
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
//input指的是使用聚合函数的时候,传过来的参数封装到了Row
if(!input.isNullAt(0)){ //考虑到传递段可能是null
val v = input.getDouble(0)
buffer(0) = buffer.getDouble(0) + v
}
}
//分区间的聚合
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
//把buffer1和buffer2的缓冲值聚合在一起 ,然后再把值写会buffer1中
buffer1(0) =buffer1.getDouble(0) + buffer2.getDouble(0)
}
//返回最终的输出值
override def evaluate(buffer: Row): Any = buffer.getDouble(0)
}
案例2 :求年龄的平均值
object UDAFDemo {
def main(args: Array[String]): Unit = {
//在sql中,聚合函数如何使用
val spark = SparkSession
.builder()
.appName("UDAFDemo")
.master("local[*]")
.getOrCreate()
import spark.implicits._
val df = spark.read.json("D:/users.json")
df.createOrReplaceTempView("user")
//注册聚合函数
spark.udf.register("myAvg",new MyAvg)
spark.sql("select myAvg(age) from user ").show
spark.close()
}
}
//定义mySum
class MyAvg extends UserDefinedAggregateFunction {
//用来定义输入的数据类型
override def inputSchema: StructType = StructType(StructField("ele",DoubleType)::Nil)
//缓冲区的类型
override def bufferSchema: StructType = StructType(StructField("sum",DoubleType) :: StructField("cnt",LongType)::Nil)
//最终聚合结果的类型
override def dataType: DataType = DoubleType
//相同的输入是否返回相同的输出
override def deterministic: Boolean = true
//对缓冲区初始化
override def initialize(buffer: MutableAggregationBuffer): Unit = {
//在缓冲集合中初始化和
buffer(0)=0D
buffer(1)=0L
}
//分区内聚合
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
//input指的是使用聚合函数的时候,传过来的参数封装到了Row
if(!input.isNullAt(0)){ //考虑到传递段可能是null
val v = input.getDouble(0)
buffer(0) = buffer.getDouble(0) + v
buffer(1) = buffer.getLong(1)+1L
}
}
//分区间的聚合
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
//把buffer1和buffer2的缓冲值聚合在一起 ,然后再把值写会buffer1中
buffer1(0) =buffer1.getDouble(0) + buffer2.getDouble(0)
buffer1(1) = buffer1.getLong(1) + buffer2.getLong(1)
}
//返回最终的输出值
override def evaluate(buffer: Row): Any = buffer.getDouble(0) / buffer.getLong(1)
}
3.1 自定义强类型聚合函数(非重点)
case class Dog(name:String,age:Int)
//定义样例类 用来缓存结果
case class ageAvg(sum:Int,count:Int){
def avg():Double = {
sum.toDouble / count
}
}
object UDAFDemo2 {
def main(args: Array[String]): Unit = {
//在sql中,聚合函数如何使用
val spark = SparkSession
.builder()
.appName("UDAFDemo2")
.master("local[*]")
.getOrCreate()
import spark.implicits._
val ds = List(Dog("大黄",6),Dog("小黄",2),Dog("中黄",4)).toDS()
//强类型的使用方式
val avg = new MyAvg2().toColumn.name("avg")
ds.select(avg)
val result:Dataset[Double] = ds.select(avg)
result.show()
spark.close()
}
}
class MyAvg2 extends Aggregator[Dog,ageAvg,Double] {
//对缓冲区进行初始化
override def zero: ageAvg = ageAvg(0,0)
//分区内聚合
override def reduce(b: ageAvg, a: Dog): ageAvg = a match {
case Dog(name,age) => ageAvg(b.sum+age,b.count+1) //如果是dog对象,则把年龄相加,个数+1
case _=> b //如果是null则原封不动的返回
}
//分区间的聚合
override def merge(b1: ageAvg, b2: ageAvg): ageAvg = {
ageAvg(b1.sum+b2.sum,b1.count+b2.count)
}
//返回最终的值
override def finish(reduction: ageAvg): Double = reduction.avg()
//对缓冲区进行编码
override def bufferEncoder: Encoder[ageAvg] = Encoders.product //如果是样例类或元组 就直接返回这个编码器
//对返回值进行编码
override def outputEncoder: Encoder[Double] = Encoders.scalaDouble
}
4 读取数据源
4.1 通用的读法
spark.read.format(“json”).load(路径)
format(“json”) 指定文件格式,如果没有则默认是parquet
4.2 专用的读法(一般用到的)
spark.read.json(“路径”) <=>spark.read.format(“json”).load(路径)
4.3 写数据到数据源
(1)通用写法:df.write.format(“json”).save(“路径”)
(2)专用写法:df.write.mode(“append”).json(“路径”)
5 JDBC
5.1 从jdbc读取数据
//通用写法
val url = "jdbc:mysql://localhost:3306/rdd"
val user = "root"
val pwd = "123456"
val df = spark.read
.option("url",url) //数据库地址
.option("user",user) //数据库用户
.option("password",pwd) //用户密码
.option("dbtable","user") //表
.format("jdbc").load()
df.show()
//专用写法
val props = new Properties()
props.put("user",user)
props.put("password",pwd)
val df = spark.read
.jdbc(url, "user", props)
df.show()
spark.close()
5.2 写数据到jdbc
(1)通用写法
object JDBCWrite {
val url = "jdbc:mysql://localhost:3306/rdd"
val user = "root"
val pwd = "123456"
def main(args: Array[String]): Unit = {
val spark = SparkSession
.builder()
.master("local[*]")
.appName("JDBCWrite")
.getOrCreate()
import spark.implicits._
val df = spark.read.json("D:/users.json")
//写到jdbc中
df.write
.format("jdbc")
.option("url",url)
.option("user",user)
.option("password",pwd)
.option("dbtable","user1")
.mode("append") //向已有数据的表中追加数据 或者 .mode(SaveMode.append)
.save()
}
}
(2)专有写法
//专有写法
val props = new Properties()
props.put("user",user)
props.put("password",pwd)
df.write
.jdbc(url,"user2",props)