1.Spark的一些基本名词解释
ClusterManager:在Standalone模式中即为Master(主节点),控制整个集群,监控Worker。在YARN模式中为资源管理器。
Worker:从节点,负责控制计算节点,启动Executor。在YARN模式中为NodeManager,负责计算节点的控制。
Driver:运行Application的main()函数并创建SparkContext。
Executor:执行器,在worker node上执行任务的组件、用于启动线程池运行任务。每个Application拥有独立的一组Executors。
SparkContext:整个应用的上下文,控制应用的生命周期。
RDD:Spark的基本计算单元,一组RDD可形成执行的有向无环图RDD Graph。
DAG Scheduler:实现将Spark作业分解成一到多个Stage,每个Stage根据RDD的Partition个数决定Task的个数,然后生成相应的Task set放到TaskScheduler中。
TaskScheduler:将任务(Task)分发给Executor执行。
Stage:一个Spark作业一般包含一到多个Stage。
Task:一个Stage包含一到多个Task,通过多个Task实现并行运行的功能。
Transformations:转换(Transformations) (如:map, filter, groupBy, join等),Transformations操作是Lazy的,也就是说从一个RDD转换生成另一个RDD的操作不是马上执行,Spark在遇到Transformations操作时只会记录需要这样的操作,并不会去执行,需要等到有Actions操作的时候才会真正启动计算过程进行计算。
Actions:操作(Actions) (如:count, collect, save等),Actions操作会返回结果或把RDD数据写到存储系统中。Actions是触发Spark启动计算的动因。
SparkEnv:线程级别的上下文,存储运行时的重要组件的引用。
SparkEnv内创建并包含如下一些重要组件的引用。
MapOutPutTracker:负责Shuffle元信息的存储。
BroadcastManager:负责广播变量的控制与元信息的存储。
BlockManager:负责存储管理、创建和查找块。
MetricsSystem:监控运行时性能指标信息。
SparkConf:负责存储配置信息。
2. Spark的基本编程
1. SparkContext
2. RDD
RDD是弹性分布式数据集,是spark的基本计算单位。SparkRDD支持两种类型的操作,Transformation,Action。如果更加细微的划分,还有输入算子(textFile,parallelize),转换算子(map…),缓存算子(cache、persist),输出算子(foreach、count、save等)。
def SparkOps: Unit = {
val conf = new SparkConf()
.setAppName("SparkTransformation")
.setMaster("local[*]")
val sc = new SparkContext(conf)
//调用算子方法
sc.stop()
}
3. Transformation( 转换算子)
3.1.1 map(一对一)
def mapOps(sc: SparkContext): Unit = {
val list = 1 to 7
val listRDD = sc.parallelize(list)
val retRDD = listRDD.map(_ * 7)
retRDD.foreach(println)
}
3.1.2 flatmap(一对多)
def flatmapOps(sc:SparkContext):Unit={
val list = List(
"yan xin xin",
"zhai zhao pin"
)
val listRDD = sc.parallelize(list)
val wordsRDD = listRDD.flatMap(line => line.split("\\s+"))
wordsRDD.foreach(println)
}
3.1.3 filter(过滤)
/*
stu
id name age gender
*/
def filterOps(sc: SparkContext): Unit = {
val stuList = List(
"1 AA 18 0",
"2 bb 28 1",
"3 CC 16 0",
"4 DD 23 1"
)
val stuRDD:RDD[String] = sc.parallelize(stuList)
val girls = stuRDD.filter(stu => {
// val fields = stu.split("\\s+")
// val gender = fields(3)
// gender == "0"
stu.endsWith("0")
})
girls.foreach(println)
}
3.1.4 sample(抽样算子)
def sampleOps(sc:SparkContext): Unit = {
val list = 1 to 10000
val listRDD = sc.parallelize(list)
val sampledRDD = listRDD.sample(false, 0.2)
println("抽取样本空间的大小:" + sampledRDD.count())
}
3.1.5 union
在spark中的union操作,作用和DB中的union all的作用相同,不会去重。
//作用类似于union all
def unionOps(sc: SparkContext): Unit = {
val listRDD1 = sc.parallelize(1 to 5)
val listRDD2 = sc.parallelize(3 to 10)
val unionRDD = listRDD1.union(listRDD2)
unionRDD.foreach(println)
}
3.1.6 join
表关联操作,这里的表是泛化的概念,只要是数据集都可以理解为表
join有哪些操作:
交叉连接:across join 写sql的时候有表的关联,但是没有on的连接字段,这会造成笛卡尔积。
3.1.7 groupbykey
对RDD中的数据按照key进行分组,首先必须要有key,数据类型必须是(key,value)经过分组之后相同的数据拉到一起,组成一个集合
(k,v).groupByKye()–>(k,Iterable[v])
def gbkOps(sc: SparkContext): Unit = {
val stu = List(
"1 鹿晗 22 bd01",
"2 杨幂 25 bd02",
"3 范冰冰 24 bd01",
"4 梁朝伟 18 bd03",
"5 黄渤 18 bd01",
"4 张震 18 bd02"
)
//按照班级进行分组 -->sql中的groupBy
val stuRDD:RDD[String] = sc.parallelize(stu)
val class2Info:RDD[(String, String)] = stuRDD.map(stuLine => {
val clazz = stuLine.substring(stuLine.indexOf("bd"))
val info = stuLine.substring(0, stuLine.indexOf("bd")).trim
(clazz, info)
})
val gbkRDD:RDD[(String, Iterable[String])] = class2Info.groupByKey()
gbkRDD.foreach{case (clazz, infos) => {
/*for(info <- infos) {
println(s"${clazz} ---> ${info}")
}*/
println(s"${clazz} ---> ${infos}")
}}
println("-----------------------------------------")
//ClassTag是类型的标记接口
val gbRDD:RDD[(String, Iterable[String])] = stuRDD.groupBy(stuLine => stuLine.substring(stuLine.indexOf("bd")))(
ClassTag.Object.asInstanceOf[ClassTag[String]]
)
gbRDD.foreach{case (clazz, infos) => {
println(s"${clazz} ===> ${infos}")
}}
}
3.1.8 reducebykey
按key进行reduce操作
def rbkOps(sc: SparkContext): Unit = {
val list = List(
"yan xin xin",
"zhai zhao pin"
)
val listRDD = sc.parallelize(list)
val wordsRDD = listRDD.flatMap(line => line.split("\\s+"))
val pairsRDD:RDD[(String, Int)]= wordsRDD.map((_, 1))
val rbkRDD:RDD[(String, Int)] = pairsRDD.reduceByKey(_+_)
rbkRDD.foreach(println)
}
3.1.9 combinebykey
combinebykey模拟Reducebykey
object _02CombineByKey2ReduceByKeyOps {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("cbk2rbk")
.setMaster("local[*]")
val sc = new SparkContext(conf)
val list = List(
"hello you hello me",
"hello you shit me",
"hello you oh shit",
"me you hello me"
)
val listRDD = sc.parallelize(list)
val pairsRDD:RDD[(String, Int)] = listRDD.flatMap(_.split("\\s+")).map((_, 1))
//reduceByKey
println("-----------传统的rbk操作--------------")
pairsRDD.reduceByKey(_+_).foreach(println)
println("-----------combineByKey模拟rbk操作--------------")
pairsRDD.combineByKey(createCombiner, mergeValue, mergeCombiners)
.foreach(println)
sc.stop()
}
/**求:1 + 10
* var sum1 = 1
*
* for(i <- 2 to 10) {
* sum1 = sum1 + i
* }
* var sum2 = 0
* for(i <- 1 to 10) {
* sum2 = sum2 + i
* }
* 初始化聚合的结果类型
*/
def createCombiner(value:Int):Int = {
value
}
//分区内的聚合操作(map端的操作)
def mergeValue(sum:Int, value:Int):Int = {
sum + value
}
//分区间的聚合操作(reduce端的操作)
def mergeCombiners(sum1:Int, sum2:Int): Int = {
sum1 + sum2
}
}
```
combinebykey模拟groupbykey
2. combineByKey模拟groupByKey
```scala
object _03CombineByKey2GroupByKeyOps {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("cbk2gbk")
.setMaster("local[2]")//thread -- task--->partition(block)
val sc = new SparkContext(conf)
val stu=list(
"1 鹿晗 22 bd01",
"2 杨幂 25 bd02",
"3 范冰冰 24 bd01",
"4 梁朝伟 18 bd03",
"5 黄渤 18 bd01",
"4 张震 18 bd02"
)
//按照班级进行分组 -->sql中的groupBy
val stuRDD:RDD[String] = sc.parallelize(stu)
val class2Info:RDD[(String, String)] = stuRDD.map(stuLine => {
val clazz = stuLine.substring(stuLine.indexOf("bd"))
(clazz, stuLine)
})
class2Info.saveAsTextFile("file:///E:/data/out/cbk")
val gbkRDD:RDD[(String, Iterable[String])] = class2Info.groupByKey()
println("-----------传统的gbk操作--------------")
gbkRDD.foreach{case (clazz, infos) => {
println(s"${clazz} ---> ${infos}")
}}
println("-----------combineByKey模拟gbk操作--------------")
class2Info.combineByKey(
(info:String) => createCombiner(info),
(buffer:ArrayBuffer[String], info:String) => mergeValue(buffer, info),
(buffer1:ArrayBuffer[String], buffer2:ArrayBuffer[String]) => mergeCombiners(buffer1, buffer2),
new MyPartitioner(3))//该分区动作不是combineByKey数据输入进来是对数据做分区,经过combineByKey计算完毕之后将结果做分区
.saveAsTextFile("file:///E:/data/out/cbk1")
sc.stop()
}
/*
初始化操作,确定聚合操作之后的结果类型
在每一个分区内相同的key,需要调用一次该操作,并将其中的一个元素用于初始化操作
*/
def createCombiner(info:String):ArrayBuffer[String] = {
println("----createCombiner------>info: " + info)
val ab = ArrayBuffer[String]()
ab.append(info)
ab
}
/**
* 分区内的相同key的聚合操作
*
*/
def mergeValue(ab:ArrayBuffer[String], info:String):ArrayBuffer[String] = {
println(s"----mergeValue------>ab:${ab.mkString(",")}《===》info: ${info}")
ab.append(info)
ab
}
/**
* 分区间的相同key的聚合操作
*/
def mergeCombiners(ab1:ArrayBuffer[String], ab2:ArrayBuffer[String]):ArrayBuffer[String] = {
println(s"----mergeCombiners------>ab1:${ab1.mkString(",")}《===》ab2:${ab2.mkString(",")}")
ab1 ++ ab2
}
}
class MyPartitioner(partitions:Int) extends Partitioner {
override def numPartitions = partitions
override def getPartition(key: Any) = {
val ret = key match {
case clazz:String => {
//bd-1901-wh
val city = clazz.substring(clazz.lastIndexOf("-") + 1)
city match {
· case "wh" => 0
case "bj" => 1
case "sz" => 2
}
}
case _ => 0
}
ret
}
}