package org.apache.spark.examples
import DsFilter.Student
import org.apache.spark.{HashPartitioner, Partitioner}
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.sql._
import org.apache.spark.sql.types.StructType
object DsFilter {
def main(args: Array[String]): Unit = {
System.setProperty("hadoop.home.dir","E:\\office\\hadoop-2.7.1") //如果读取不到环境变量,设置这个
val spark: SparkSession = SparkSession
.builder
.appName("Spark Examples")
.master("local[*]") //我的机器是4core
.getOrCreate()
var list = Seq[Student]()
for (i <- 1 to 10){
list= list :+ Student("cc",i,i.toString)
list= list :+ Student("chi",i,i.toString)
list= list:+Student("zbf",i,i.toString)
list= list:+Student("dd",i,i.toString)
}//创建40条记录
val userList=Seq(User("cc",true),User("chi",true),User("dd",false),User("zbf",false))
val rdd: RDD[Student] = spark.sparkContext.parallelize(list) //parallelize(list , numpartition) 这里可以指定分区数,默认是hash分区,既然是hash分区那么可能分配不均
val partitions: Long = rdd.count()
println(s"rdd中的数据量=${partitions}") // 40
printRDDPartition(rdd)
/**
* 第0个分区-------(cc,1,1),(chi,1,1),(zbf,1,1),(dd,1,1),(cc,2,2),(chi,2,2),(zbf,2,2),(dd,2,2),(cc,3,3),(chi,3,3)
* 第1个分区-------(zbf,3,3),(dd,3,3),(cc,4,4),(chi,4,4),(zbf,4,4),(dd,4,4),(cc,5,5),(chi,5,5),(zbf,5,5),(dd,5,5)
* 第2个分区-------(cc,6,6),(chi,6,6),(zbf,6,6),(dd,6,6),(cc,7,7),(chi,7,7),(zbf,7,7),(dd,7,7),(cc,8,8),(chi,8,8)
* 第3个分区-------(zbf,8,8),(dd,8,8),(cc,9,9),(chi,9,9),(zbf,9,9),(dd,9,9),(cc,10,10),(chi,10,10),(zbf,10,10),(dd,10,10)
*/
val studentName: Array[String] = rdd.map(_.name).collect().distinct //获取有多少个name, 目前是四个
val hashparitionedRddTurple: RDD[(String, Student)] = rdd.map(x=>(x.name,x)).partitionBy(new HashPartitioner(4))
val hashpartitonedRDd: RDD[Student] = hashparitionedRddTurple.map(_._2) //里面的元素还是那40条 但是存放位置不一样了
println("hashpartitonedRDd分区后的数据")
printRDDPartition(hashpartitonedRDd)
/**
* 第0个分区-------(cc,1,1),(chi,1,1),(dd,1,1),(cc,2,2),(chi,2,2),(dd,2,2),(cc,3,3),(chi,3,3),(dd,3,3),(cc,4,4),(chi,4,4),(dd,4,4),(cc,5,5),(chi,5,5),(dd,5,5),(cc,6,6),(chi,6,6),(dd,6,6),(cc,7,7),(chi,7,7),(dd,7,7),(cc,8,8),(chi,8,8),(dd,8,8),(cc,9,9),(chi,9,9),(dd,9,9),(cc,10,10),(chi,10,10),(dd,10,10)
* 第1个分区-------
* 第2个分区-------(zbf,1,1),(zbf,2,2),(zbf,3,3),(zbf,4,4),(zbf,5,5),(zbf,6,6),(zbf,7,7),(zbf,8,8),(zbf,9,9),(zbf,10,10)
* 第3个分区-------
*/
val paritionedRddTurple: RDD[(String, Student)] = rdd.map(x=>(x.name,x)).partitionBy(new MyPartition(studentName))
val partitonedRDd: RDD[Student] = paritionedRddTurple.map(_._2) //里面的元素还是那40条 但是存放位置不一样了
println("rdd分区后的数据")
printRDDPartition(partitonedRDd)
/**
* 第0个分区-------(cc,1,1),(cc,2,2),(cc,3,3),(cc,4,4),(cc,5,5),(cc,6,6),(cc,7,7),(cc,8,8),(cc,9,9),(cc,10,10)
* 第1个分区-------(chi,1,1),(chi,2,2),(chi,3,3),(chi,4,4),(chi,5,5),(chi,6,6),(chi,7,7),(chi,8,8),(chi,9,9),(chi,10,10)
* 第2个分区-------(zbf,1,1),(zbf,2,2),(zbf,3,3),(zbf,4,4),(zbf,5,5),(zbf,6,6),(zbf,7,7),(zbf,8,8),(zbf,9,9),(zbf,10,10)
* 第3个分区-------(dd,1,1),(dd,2,2),(dd,3,3),(dd,4,4),(dd,5,5),(dd,6,6),(dd,7,7),(dd,8,8),(dd,9,9),(dd,10,10)
*/
/* rdd中的数据其实看似有序,但是对于我们来说大部分无序,所以需要我们自己定义分区器,
自定义分区的作用:一个分区做一件事,
①比如我要输出文件有序第一个文件是cc第二个是chi第三个是zbf,现在4个分区最后输出文件是4个
②每个分区数据有不同的逻辑 比如我想cc的age+1,chi的age+2
rdd.foreachPartition(iter=>{
val list: List[Student] = iter.toList
if(list.head.name.equals("cc")){}
if(list.head.name.equals("chi")){}
})
// 下面是lowb方法,先过滤出需要的rdd然后再处理
rdd.filter(_.name.equals("cc"))
rdd.filter(_.name.equals("chi"))
多嘴说一句
rdd.foreachPartition作用 比如我现在rdd是4个分区,数据是40个
如果涉及到数据库连接建议用foreaparition 此时只需要创建4个连接,每个连接对应一个分区
如果用foreach 则需要创建40个连接 每个连接对应一条数据
有人会说我在最外面建立一个就好对应整个数据集合,不可能,这个涉及到序列化问题,简单来说上面两个连接都是在worker端建立
//driver端
建立连接 对应整个数据 然后driver到work相当于两个世界,要把driver的数据到work相当于快递要打包(序列化),目前连接不能序列化
foreach/partition(
//work端
//建立连接 对应一条数据或者一个分区
)
*/
import spark.implicits._ //导入隐世转换,因为rdd是org.apache.spark.rdd包下的本身没有toDF方法,所以导入spark.implicits._包,才有rdd.todf
val ds: Dataset[User] = spark.createDataset(userList)
val rddToDf: DataFrame = rdd.toDF() //注意 rdd中是student样例类所以可以直接转化为,如果是List(Seq("cc",1,"1"),Seq("cc",2,"2")) 要toDF("name","age","score")
rddToDf.show(5) //action算子,自己测试时可以用,实际生产个人不建议频繁用
/**
* +----+---+-----+
* |name|age|score|
* +----+---+-----+
* | cc| 1| 1|
* | chi| 1| 1|
* | zbf| 1| 1|
* | dd| 1| 1|
* | cc| 2| 2|
* +----+---+-----+
* only showing top 5 rows
*/
//df的repartition
val dfPartitionedRdd: RDD[Student] = rddToDf.repartition(4,new Column("name")).rdd.map(x=>Student(x.getString(0),x.getInt(1),x.getString(2)))
println("rdd转化成df后repartition后每个rdd分区")
printRDDPartition(dfPartitionedRdd)
/** 说明下,这里df.repartition 根据name分区,是根据name 的hashcode值,以上述为例
* cc.hashcode=1 chi.hashcode=1 dd.hashcode=0 zbf.hashcode=3
* 取余后 dd分配到0分区,cc和chi分配到1分区,zbf分配到3分区,
* df的reparition 和 rdd.partition都是hash分区
* 第0个分区-------(dd,1,1),(dd,2,2),(dd,3,3),(dd,4,4),(dd,5,5),(dd,6,6),(dd,7,7),(dd,8,8),(dd,9,9),(dd,10,10)
* 第1个分区-------(cc,1,1),(chi,1,1),(cc,2,2),(chi,2,2),(cc,3,3),(chi,3,3),(cc,4,4),(chi,4,4),(cc,5,5),(chi,5,5),(cc,6,6),(chi,6,6),(cc,7,7),(chi,7,7),(cc,8,8),(chi,8,8),(cc,9,9),(chi,9,9),(cc,10,10),(chi,10,10)
* 第2个分区-------
* 第3个分区-------(zbf,1,1),(zbf,2,2),(zbf,3,3),(zbf,4,4),(zbf,5,5),(zbf,6,6),(zbf,7,7),(zbf,8,8),(zbf,9,9),(zbf,10,10)
*/
rddToDf.describe("name","age","score").show()
/** //科学计数法
* +-------+----+------------------+
* |summary|name| age|
* +-------+----+------------------+
* | count| 30| 30|
* | mean|null| 5.5|
* | stddev|null|2.9213837061606074|
* | min| cc| 1|
* | max| zbf| 10|
* +-------+----+------------------+
*/
println(" rddToDf.printSchema()=")
rddToDf.printSchema()
/**
* root
* |-- name: string (nullable = true)
* |-- age: integer (nullable = false)
* |-- score: string (nullable = true)
*/
val column1: Column = rddToDf.apply("name")
println(s"rddToDf.apply=${column1}")
/**
* rddToDf.apply=name
*/
val schema: StructType = rddToDf.schema
println(s" rddToDf.schema=${schema.mkString("|")}")
/**
* rddToDf.schema=StructField(name,StringType,true)|StructField(age,IntegerType,false)|StructField(score,StringType,true)
*/
val columns: Array[String] = rddToDf.columns
println(s" rddToDf.columns=${columns.mkString("|")}")
/**
* rddToDf.columns=name|age|score
*/
val dtypes: Array[(String, String)] = rddToDf.dtypes
println(s" rddToDf.dtypes=${dtypes.mkString("|")}")
/**
* rddToDf.dtypes=(name,StringType)|(age,IntegerType)|(score,StringType)
*/
val column: Column = rddToDf.col("name")
println(s"column=${column}")
/**
* column=name
*/
rddToDf.explain() //目前层次未到 有点难看懂
rddToDf.createTempView("Student") //创建一张临时表
val df: DataFrame = spark.sql("select * from Student")
df.show(5)
/**
* +----+---+-----+
* |name|age|score|
* +----+---+-----+
* | cc| 1| 1|
* | chi| 1| 1|
* | zbf| 1| 1|
* | dd| 1| 1|
* | cc| 2| 2|
* +----+---+-----+
* only showing top 5 rows
*/
import org.apache.spark.sql.functions._ //下面这种属于DSL风格sql查询,方法也是再functions._下的包里才有
df.filter(col("name").equalTo("cc")).show(5)
/**
* +----+---+-----+
* |name|age|score|
* +----+---+-----+
* | cc| 1| 1|
* | cc| 2| 2|
* | cc| 3| 3|
* | cc| 4| 4|
* | cc| 5| 5|
* +----+---+-----+
*/
df.filter("age>5").show(5)
/**
* +----+---+-----+
* |name|age|score|
* +----+---+-----+
* | cc| 6| 6|
* | chi| 6| 6|
* | zbf| 6| 6|
* | dd| 6| 6|
* | cc| 7| 7|
* +----+---+-----+
* only showing top 5 rows
*/
df.groupBy("name").agg(count(col("age")),avg(col("score"))).show(3)
/**
* +----+----------+----------+
* |name|count(age)|avg(score)|
* +----+----------+----------+
* | zbf| 10| 5.5|
* | cc| 10| 5.5|
* | chi| 10| 5.5|
* +----+----------+----------+
* only showing top 3 rows
*/
df.groupBy("name").agg(max(col("age"))as("maxAge"),avg(col("score")).as("avgScore")).show(3)
/** //取别名
* +----+------+--------+
* |name|maxAge|avgScore|
* +----+------+--------+
* | zbf| 10| 5.5|
* | cc| 10| 5.5|
* | chi| 10| 5.5|
* +----+------+--------+
* only showing top 3 rows
*/
df.cube("name","age").agg(sum("score").as("score")).orderBy(col("name"),col("score")).show(100)
/**55条记录 =40条记录+4条name=null + 10条age=null +1条name=null&&age=null
* select name ,age ,sum(score) as score from student groupby name ,age
* union select null age ,sum(score) as score from student groupby age
* union select name,null ,sum(score) as score from student groupby name
* union select null,null,sum(score) as score from student groupby name
* +----+----+-----+
* |name| age|score|
* +----+----+-----+
* |null| 1| 4.0|
* |null| 2| 8.0|
* |null| 3| 12.0|
* |null| 4| 16.0|
* |null| 5| 20.0|
* |null| 6| 24.0|
* |null| 7| 28.0|
* |null| 8| 32.0|
* |null| 9| 36.0|
* |null| 10| 40.0|
* |null|null|220.0|
* | cc| 1| 1.0|
* | cc| 2| 2.0|
* | cc| 3| 3.0|
* | cc| 4| 4.0|
* | cc| 5| 5.0|
* | cc| 6| 6.0|
* | cc| 7| 7.0|
* | cc| 8| 8.0|
* | cc| 9| 9.0|
* | cc| 10| 10.0|
* | cc|null| 55.0|
* | chi| 1| 1.0|
* | chi| 2| 2.0|
* | chi| 3| 3.0|
* | chi| 4| 4.0|
* | chi| 5| 5.0|
* | chi| 6| 6.0|
* | chi| 7| 7.0|
* | chi| 8| 8.0|
* | chi| 9| 9.0|
* | chi| 10| 10.0|
* | chi|null| 55.0|
* | dd| 1| 1.0|
* | dd| 2| 2.0|
* | dd| 3| 3.0|
* | dd| 4| 4.0|
* | dd| 5| 5.0|
* | dd| 6| 6.0|
* | dd| 7| 7.0|
* | dd| 8| 8.0|
* | dd| 9| 9.0|
* | dd| 10| 10.0|
* | dd|null| 55.0|
* | zbf| 1| 1.0|
* | zbf| 2| 2.0|
* | zbf| 3| 3.0|
* | zbf| 4| 4.0|
* | zbf| 5| 5.0|
* | zbf| 6| 6.0|
* | zbf| 7| 7.0|
* | zbf| 8| 8.0|
* | zbf| 9| 9.0|
* | zbf| 10| 10.0|
* | zbf|null| 55.0|
* +----+----+-----+
*/
df.rollup("name","age").agg(sum("score").as("score")).orderBy(col("name"),col("score")).show(100)
/**45条记录=40 + 4条name分组 和1条 不分组
* select name ,age ,sum(score) as score from student groupby name ,age
* union select name,null ,sum(score) as score from student groupby name
* union select null,null,sum(score) as score from student groupby name
* !!!!!区别 个人理解!!!!!!
* cube立方三维,借用kylin的思想就是从多个角度去看分组,如果我分组维度有3个,name,age,score,那么先减去1个维度获得【(name,age)(name,score)(age.score)】,然后减去2个维度获得[(name),(age),(score)],再减去3个维度【】
* rollup,向上 注意比如我name age 分组,(注意顺序!!)那么我还是逐渐减少分组维度,向上递减,先减去age这个维度获得4条记录,然后减去name这个维度 获得1条记录
* +----+----+-----+
* |name| age|score|
* +----+----+-----+
* |null|null|220.0|
* | cc| 1| 1.0|
* | cc| 2| 2.0|
* | cc| 3| 3.0|
* | cc| 4| 4.0|
* | cc| 5| 5.0|
* | cc| 6| 6.0|
* | cc| 7| 7.0|
* | cc| 8| 8.0|
* | cc| 9| 9.0|
* | cc| 10| 10.0|
* | cc|null| 55.0|
* | chi| 1| 1.0|
* | chi| 2| 2.0|
* | chi| 3| 3.0|
* | chi| 4| 4.0|
* | chi| 5| 5.0|
* | chi| 6| 6.0|
* | chi| 7| 7.0|
* | chi| 8| 8.0|
* | chi| 9| 9.0|
* | chi| 10| 10.0|
* | chi|null| 55.0|
* | dd| 1| 1.0|
* | dd| 2| 2.0|
* | dd| 3| 3.0|
* | dd| 4| 4.0|
* | dd| 5| 5.0|
* | dd| 6| 6.0|
* | dd| 7| 7.0|
* | dd| 8| 8.0|
* | dd| 9| 9.0|
* | dd| 10| 10.0|
* | dd|null| 55.0|
* | zbf| 1| 1.0|
* | zbf| 2| 2.0|
* | zbf| 3| 3.0|
* | zbf| 4| 4.0|
* | zbf| 5| 5.0|
* | zbf| 6| 6.0|
* | zbf| 7| 7.0|
* | zbf| 8| 8.0|
* | zbf| 9| 9.0|
* | zbf| 10| 10.0|
* | zbf|null| 55.0|
* +----+----+-----+
*/
df.withColumn("newname",col("name")).show(3)
/** 新增一列
* +----+---+-----+-------+
* |name|age|score|newname|
* +----+---+-----+-------+
* | cc| 1| 1| cc|
* | chi| 1| 1| chi|
* | zbf| 1| 1| zbf|
* +----+---+-----+-------+
* only showing top 3 rows
*/
df.withColumn("newname",col("name")).select("name","age","score").show(3)
/**
* +----+---+-----+-------+
* |name|age|score|newname|
* +----+---+-----+-------+
* | cc| 1| 1| cc|
* | chi| 1| 1| chi|
* | zbf| 1| 1| zbf|
* +----+---+-----+-------+
* only showing top 3 rows
*/
df.select(col("name").equalTo("cc")).show(5)
/**这个刚好和上面对应 cc chi zbf dd cc 刚好是true false false false true
* +-----------+
* |(name = cc)|
* +-----------+
* | true|
* | false|
* | false|
* | false|
* | true|
* +-----------+
* only showing top 5 rows
*/
df.select(col("age").lt(5)).show(5) //小于
/**
* +---------+
* |(age < 5)|
* +---------+
* | true|
* | true|
* | true|
* | true|
* | true|
* +---------+
* only showing top 5 rows
*/
val dsBC: Broadcast[Dataset[User]] = spark.sparkContext.broadcast(ds)
df.join(ds,df("name")===ds("name"),"outer").orderBy(col("score")).show(4)
/**两张表关联
* 'inner', 'outer', 'full', 'fullouter', 'full_outer', 'leftouter', 'left', 'left_outer',
* 'rightouter', 'right', 'right_outer', 'leftsemi', 'left_semi', 'leftanti', 'left_anti', 'cross'
* +----+---+-----+----+-----+
* |name|age|score|name|shuai|
* +----+---+-----+----+-----+
* | cc| 1| 1| cc| true|
* | chi| 1| 1| chi| true|
* | zbf| 1| 1| zbf|false|
* | dd| 1| 1| dd|false|
* +----+---+-----+----+-----+
* only showing top 10 rows
*/
}
//打印输出
def printRDDPartition(rdd:RDD[Student]): Unit ={
val array: Array[Array[Student]] = rdd.glom().collect()
for(i <- 0 until array.size){
println(s"第${i}个分区-------${array(i).map(x=>{
(x.name,x.age,x.score)
}).mkString(",")}")
}
}
case class Student(name: String, age: Int,score:String){}
case class User(name: String, shuai:Boolean){}
class MyPartition(studentName:Array[String]) extends Partitioner{
override def numPartitions: Int = studentName.size //分区数
override def getPartition(key: Any): Int = { //这个方法是那40条数据,每条数据都会把name 传进来返回一个分区号
val index: Array[(String, Int)] = studentName.zipWithIndex //给每个名字固定一个下标 =>其实也就是第几个分区
val tuples: Array[(String, Int)] = index.filter(_._1.equals(key.toString)) //在index数组中找到传来名字的属于哪个分区
tuples.head._2 //head是因为 数据去重了只有一条
}
}
}