spark项目
gitee.com/elaiza/spar…
rdd算子
blog.csdn.net/u013998466/…
rdd概述
1. RDD
-弹性分布式数据集
-弹性
*存储
*计算与容错
*分区 一个分区对应一个Executor
-分布式
不同分区中的数据会分配给集群中的不同服务器节点进行计算
-数据集
和集合不一样,没有存放数据,存的是计算逻辑
2.RDD的五个特性
-一组分区 getPartitions
-分区计算函数 compute
-RDD之间的依赖 getDependence
-分区器Partitioner(对 KV 类型的RDD)
-数据存储的优先位置 getreferedLocation
3.RDD创建的方式
-通过内存集合创建
-读取外部文件创建
-通过RDD的转换算子
4.创建RDD分区方式
-通过内存集合创建
*默认的分区方式
取决于分配给当期应用的CUP核数
*指定分区
def posttions(length:Long,nunmSlices:Int):Ierator[(Int,Int)] = {
...
}
-读取外部文件创建
*默认的分区方式
取决于分配给当前应用的CUP核数和2取最小值
*指定分区
>FileInputFormat getSplits 切片
>LineRecorReader next 读取
-通过RDD的转换算子
5.RDD的常用算子
-转换算子
转换算子执行完成后,会创建新的RDD,不会马上执行计算
shuffle 就是 把分区里的数据 写入到磁盘,又下个RDD再读磁盘的数据
-行动算子
行动算子执后,才会触发计算
reduce
对RDD中的元素进行聚合
*collect.foreach和foreach
>collect.foreach
将每一个Excutor中的数据收集到Driver,形成一个新的数组
.foreach 不是一个算子,是集合的方法,是对数组中的元素进行遍历
>foreach
对RDD中的元素进行遍历
*count
获取RDD中元素的个数
*countByKey
获取RDD中每个key对应的元素个数
*first
获取RDD中第一个个元素
*take
获取RDD中前几个元素
*takeOrdered
获取排序后的RDD中的前几个元素
*aggreate&fold
>aggreateByKey 处理kv类型的RDD,并且在进行分区间聚合的时候,初始值不参与计算
>fold 是 aggreate的简化版
*save相关的算子
>saveASsTextFile
>saveAsObjectFile
>saveAsSequenceFile(只针对kv类型的RDD)
6.序列化以及闭包检查
-为什么要序列化
因为在spark程序中,算子相关的操作在Excutor上执行,算子之外的代码在Dricer端执行
在执行有些算子的时候,需要使用到Driver里面定义的数据,这就涉及到了跨进程或者跨节点之间的通信
所以要求传递给Exctor中的数组所属的类型必须实现Serializable接口
-如何判断是否实现了序列化的接口
在作业job提交之前,其中有一行代码 val cheanF = sc.clean(f),用于进行闭包检查
之所以叫闭包检查,是因为在当前函数的内部访问了外部函数的变量,属于闭包的形式。
如果算子的参数是函数的形式就会存在这种情况
7.查看RDD的血缘关系以及依赖关系
-血缘关系
toDebugString
-依赖关系
>dependencies
>窄依赖
父RDD一个分区中的数据,还是交给子RDD的一个分区处理
>宽依赖
父RDD一个分区中的数据,交给子RDD的多个分区处理
8.Spark的Job调度
-集群(Standalone|Yarn)
*一个Spark集群可以同时运行多个Spark应用
-应用
*我们所编写的完成某些功能的程序
*一个应用可以并发的运行多个Job
-Job
*Job对应着我们应用中的行动算子,每次执行一个行动算子,都会提交一个Job
*一个Job由多个Stage组成
-Stage
*一个宽依赖做一次阶段的划分
*阶段的个数 = 宽依赖个数 + 1
*一个Stage由多个Task组成
-Task
*每一个阶段最后一个RDD的分区数,就是当前阶段的Task个数
9.RDD的持久化
-cache
底层调用的就是persist,默认存储在内存中
-persist
可以通过参数指定存储级别
-checkpoint
*可以当做缓存理解,存储在HDFS上,更稳定
*为了避免容错执行时间过长
-缓存不会切断血缘,但是检查点会切断血缘
10.数据的读取和保存
-textFile
-sequenceFile
-objectFile
-Json
本质还是通过textFile读取文本,对读到的内容进行处理
-HDFS
-MySQL
map-------mapPartition
foreach---foreachPartition
11.在Spark里面,有三大结构
-RDD
弹性分布式数据集
-累加器
分布式共享只写变量
-广播变量
分布式共享只读变量
------------------------------------------------------------------------------------------------
1.SparkSQL和Hive对比
-hive---引擎---MR
-SparkSQL---引擎---spark
2.SparkSQL对数据集的抽象
-RDD
-DataFrame
*spark.read.不同类型的文件
*通过SQL风格操作df,需要创建临时视图
*通过DSL风格操作df,不需要创建临时视图
*DF->RDD rdd
*RDD->DF toDF
*在编程的时候,需要导入 import SparkSession对象名.implicits._
-DataSet(强类型)
*RDD->DS toDS
*DS->RDD rdd
*DF->DS as[类型]
*DS->DF toDF
3.用户自定义函数
-UDF
一进一出
-UDAF
多进一出
*弱类型 继承UserDefinedAggregateFunction
*强类型 Aggregator[输入,缓存,输出]
-UDTF
一进多出
4.文件的加载与数据保存
-spark.read.format("类型").load("路径")
-df.writer.format("类型").save("路径")
------------------------------------------------------------------------------------------------
1.SparkStreaming
-是Spark的内置模块,主要用于实时计算
-微批次流式实时计算框架
-离线和实时
数据处理的延迟
-批和流式
2.架构图
3.背压机制
4.创建方式
-DStream 是SparkStreaming对处理的数据集的一个抽象
-读取指定端口数据创建DS
-通过RDD队列创建DS(了解)
-通过定义Receiver读取指定数据源数据创建DS
-通过读取kafka数据源创建DS
*spark-streaming-kafka-0-8(Kafka 0.8 support is deprecated as of Spark 2.3.0)
>Receiver DStream
&默认情况下,offset维护在zk中
>Direct DStream
&默认情况下,offset维护在checkpoint检查点,需要改变SparkStreamingContext的创建方式
&可以手动指定offset维护位置,为了保证数据的精准一致性,一般维护在有事务的存储上
*spark-streaming-kafka-0-10
>Direct DStream
5.DStream算子
-转换算子
*无状态的转换操作
transform
map\flatmap...
*有状态的转换操作
第一个参数Seq,相同的key对应的value的集合
第二个参数Option,相同的key对应的状态(上一个采集周期计算的结果)
&updateStateByKey(Seq,Option)
&window
-输出算子
6.Spark几个重要的关系对比
SparkCore SparkSQL SparkStreaming
程序执行的入口点 SparkContext(sc) SparkSession(sparkt) StreamingContext(ssc)
对数据的抽象 RDD DF|DS(DataSet) DS(DStream)
7.内核源码分析
-第一部分:将App部署到Yarn服务器
-第二部分:Job以及任务调度过程
App->Job->Stage->Task
-第三部分:提交Task到Executor
-第四部分:Shuffle过程分析
==============提交Task到Executor=============
App->Job->Stage->Task
1.Driver端任务提交 org.apache.spark.scheduler.DAGScheduler
-submitMissingTasks
*taskScheduler.submitTasks(new TaskSet)
>val manager = createTaskSetManager(taskSet, maxTaskFailures)
>schedulableBuilder.addTaskSetManager(manager, manager.taskSet.properties)
>backend.reviveOffers()
&driverEndpoint.send(ReviveOffers)
&Driver终端会通过receive的方法,接收消息并对其进行处理
#case ReviveOffers =>makeOffers()
~val activeExecutors = executorDataMap.filterKeys(executorIsAlive)
~val workOffers = activeExecutors.map
~运行Task launchTasks(scheduler.resourceOffers(workOffers))
+val serializedTask = ser.serialize(task)
+executorData.executorEndpoint.send(LaunchTask(new SerializableBuffer(serializedTask)))
2.Executor端接收Task并运行
-receive
*case LaunchTask(data)
>val taskDesc = ser.deserialize[TaskDescription](data.value)
>executor.launchTask
&threadPool.execute(tr)
#TaskRunner.run
~task.run
+runTask(context)
++ShuffleMapTask
++ResultTask
开启shell
./bin/spark-shell
./bin/spark-shell --master spark://master:7077
./bin/spark-shell --master yarn
提交spark任务
./run-example SparkPi 1 --master local[2]
./bin/spark-submit \
--master spark://master:7077 \
--class org.apache.spark.examples.SparkPi \
./examples/jars/spark-examples_2.11-2.1.1.jar \
100
./bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode client \
./examples/jars/spark-examples_2.11-2.1.1.jar \
100
./bin/spark-submit \
--class org.apache.spark.examples.SparkPi \
--master yarn \
--deploy-mode cluster \
./examples/jars/spark-examples_2.11-2.1.1.jar \
100
Rdd
Application->job->stage->task(partition) 每一层都是 1 对 n 的关系
job: 一个行动算子就是一个job
stage: 是一个TaskSet
task(partition): 每个分区就是一个任务(每个stage里的最后一个的分区数量),发送到不同的Executor执行
k-v算子
1. partitionBy
val rdd = sc.parallelize(Array((1,'a'),(2,'b'),(3,'c'),(4,'d')), 4)
// 根据key来取模分区的分区器
val rdd2 = rdd.partitionBy(new org.apache.spark.HashPartitioner(2))
rdd2.glom.collect()
2.groupByKey
val rdd = sc.parallelize(Array("a","a","b","v","b","b")).map(x=>(x,1))
val rdd2 = rdd.groupByKey()
rdd2.map(t => (t._1,t._2.sum)).collect()
3.reduceByKey
val rdd = sc.parallelize(Array("a","a","b","v","b","b")).map(x=>(x,1))
rdd.reduceByKey(_+_).collect // 相同键的值两两相加
DataFrame(结构)
val df = spark.read.json("file:////usr/local/src/data/spark_about/demo.json")
df.createTempView("demo")
spark.sql("select * from demo").show
df.createGlobalTempView("global")
spark.sql("select * from global_temp.global").show
1.df.printSchema
2.df.select("name").show()
3.df.select($"name", $"age"+1).show()
4.df.filter($"age" <= 20).show()
5.df.groupBy("age").count().show()
import spark.implicits._
val rdd = sc.parallelize(Array("a","a","b","v","b","b"))
rdd.toDF("id")
创建样例类,根据样例类来将RDD转换为DataFrame
case class People(name:String, age:Int)
val rdd = sc.parallelize(List(("a",1),("a",3),("b",4),("b",19),("a",30),("c",1)))
val peopleRDD = rdd.map( t => { People(t._1, t._2) })
val df = peopleRDD.toDF
df.show()
val rdd = df.rdd
DataSet(结构+类型)
样例类是有结构和类型的,所以可以直接把rdd 转成 DataSet
case class People(name:String, age:Int)
val caseClassDS = Seq(People("Andy",32)).toDS()
sparksql 两大类: DataFrame和DataSet
RDD+结构 = DataFrame+类型 = DataSet
rdd -> toDF
rdd.toDF(字段名称)
rdd.toFS
DataFrame.rdd
DataSet.rdd
UDF 用户自定义函数
spark.udf.register("addName",(x:String) => "name:" + x)
val df = spark.read.json("file:////usr/local/src/data/spark_about/demo.json")
df.createTempView("info")
spark.sql("select addName(name) from info").show
UDAF 用户自定义聚合函数(弱类型)
import org.apache.spark.SparkConf
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, DoubleType, LongType, StructType}
object SparkSql_UDAF {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf()
.setMaster("local[*]")
.setAppName("SparkSql_UDAF")
val spark = SparkSession
.builder()
.config(sparkConf)
.getOrCreate()
import spark.implicits._
val udaf = new MyAgeAvgFunction
spark.udf.register("avgAge", udaf)
val rdd = spark.sparkContext.parallelize(List((1,"elaiza",24),(2,"Yw",25),(3,"ee",25)))
val frame = rdd.toDF("id","name","age")
frame.createOrReplaceTempView("info")
spark.sql("select avgAge(age) from info").show()
spark.stop()
}
}
class MyAgeAvgFunction extends UserDefinedAggregateFunction {
override def inputSchema: StructType = {
new StructType().add("age", LongType)
}
override def bufferSchema: StructType = {
new StructType().add("sum", LongType).add("count", LongType)
}
override def dataType: DataType = DoubleType
override def deterministic: Boolean = true
override def initialize(buffer: MutableAggregationBuffer): Unit = {
buffer(0) = 0L
buffer(1) = 0L
}
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
buffer(0) = buffer.getLong(0) + input.getLong(0)
buffer(1) = buffer.getLong(1) + 1
}
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
buffer1(0) = buffer1.getLong(0) + buffer2.getLong(0)
buffer1(1) = buffer1.getLong(1) + buffer2.getLong(1)
}
override def evaluate(buffer: Row): Any = {
buffer.getLong(0).toDouble / buffer.getLong(1)
}
}
UDAF 用户自定义聚合函数(强类型)
package sql
import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.Aggregator
import org.apache.spark.sql.{Encoder, Encoders, Row, SparkSession}
object SparkSql_UDAF_Class {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSql_UDAF")
val spark = SparkSession
.builder()
.config(sparkConf)
.getOrCreate()
import spark.implicits._
val udaf = new MyAgeAvgClassFunction
val avgCol = udaf.toColumn.name("agvAge")
val frame = spark.read.json("file:///Users/elaiza/Desktop/spark-demo/in/demo.json")
val ds = frame.as[UserBean]
ds.select(avgCol).show()
spark.stop()
}
}
case class UserBean (name:String, age:BigInt)
case class AvgBuffer (var sum:BigInt, var count:Int)
class MyAgeAvgClassFunction extends Aggregator[UserBean, AvgBuffer, Double] {
override def zero: AvgBuffer = {
AvgBuffer(0, 0)
}
override def reduce(b: AvgBuffer, a: UserBean): AvgBuffer = {
b.sum = b.sum + a.age
b.count = b.count + 1
b
}
override def merge(b1: AvgBuffer, b2: AvgBuffer): AvgBuffer = {
b1.sum = b1.sum + b2.sum
b1.count = b1.count + b2.count
b1
}
override def finish(reduction: AvgBuffer): Double = {
reduction.sum.toDouble / reduction.count
}
override def bufferEncoder: Encoder[AvgBuffer] = Encoders.product
override def outputEncoder: Encoder[Double] = Encoders.scalaDouble
}
神秘代码:22126-CC8C-C991-A1DA-7378