spark核心部分总结
spark-core
spark简介
- 分布式计算引擎(大数据计算框架),用来替代MapReduce
- 速度是MapReduce的一百倍(官方),实际检测大概十倍左右
- spark会尽量将数据放在内存中进行计算(cache)
- 使用DAG有向无环图 spark可以将多个MapReduce串联在一起
- 粗粒度资源调度,spark在任务执行之前会将所需要的所有资源全部申请下来
- spark生态体系
- spark-sql 将sql转换成RDD进行计算
- MLlib 机器学习
- Graphx 图计算
- spark-streaming 实时计算
运行模式
- local[] 本地运行
- 独立集群 基本已经淘汰
- yarn
- yarn-client
1.Driver在本地启动
2.一般用于上线前的测试
3.spark程序运行的日志会全部发送到Driver端,会导致本地网卡流量剧增- yarn-cluster
1.Driver在集群中启动
2.上线使用
- Mesos
RDD五大特性
RDD Resilient Distributed Dataset 弹性分布式数据集
- A list of partitions
- A function for computing each split
- A list of dependencies on other RDDs
- Optionally, a Partitioner for key-value RDDs
- Optionally, a list of preferred locations to compute each split on
- RDD由一组分区组成,每一个block块对应一个分区
- 函数实际上是作用在每一个分区上,每一个分区都会有一个task来处理
- RDD之间存在依赖关系
宽依赖
- 有shuffle的算子会产生宽依赖,分区对应关系是一对多
窄依赖
- 没有shuffle的算子, 分区对应的关系是一对一
stage
- 根据宽窄依赖划分stage (一组可以并行计算的task)
- 分区的算子必须作用在key-value格式的RDD上
- spark为task的计算提供了最佳计算位置,会尽量将task发送到数据所在的节点执行,移动计算而不是移动数据
- taskScheduler知道所有数据的位置
- task由taskScheduler发送
常用算子
转换算子
- map
- flatmap
- filter
- groupByKey
- reduceByKey
- join
- union
- sortBy
- sample
- mapValues
- mapPartition
…操作算子
- reduce
- foreach
- collect
- count
- saveAsTextFile
…
转换算子是懒执行模式,即存在于逻辑层面,实际上并没有做任何操作,只有在出现操作算子时,才会将代码运行。
缓存
- RDD中默认不保存数据
- 如果多次使用同一个RDD,可以将RDD进行缓存(懒执行)
- 使用方式
persist 方法
- MEMORY_ONLY
- MEMORY_ONLY_SER
- MEMORY_AND_DISK_SER
- 缓存的数据实际上是保存在Executor的内存或者磁盘上的,由blockmanager管理
val rdd1: RDD[String] = sc.textFile("spark/data/students.txt")
val stuRDD: RDD[String] = rdd1.map(line => {
println("map")//测试检查用,看RDD算子运行几次
line
})
//尝试不加后上面的map出现几次,相当于 stuRDD.persist(StorageLevel.MEMORY_ONLY)
stuRDD.cache()
//缓存到内存,内存足够为第一选择
// stuRDD.persist(StorageLevel.MEMORY_ONLY)
//
// 第二选择,内存加压缩数据
// stuRDD.persist(StorageLevel.MEMORY_ONLY_SER)
//
// 第三选择,内存加压缩数据加磁盘
// stuRDD.persist(StorageLevel.MEMORY_AND_DISK_SER)
//统计班级人数
stuRDD
.map(line => (line.split(",")(4),1))
.reduceByKey(_+_)
.foreach(println)
//统计性别人数
stuRDD
.map(line => (line.split(",")(3),1))
.reduceByKey(_+_)
.foreach(println)
CheckPoint
- 将RDD的数据保存到hdfs中,会切断RDD的依赖关系
- 流程
1.当第一个job执行完成之后,会向前回溯,如果有RDD做了CheckPoint,会打上一个标记
2. 重新启动一个job任务计算RDD 的数据,将RDD的数据保存到hdfs
- 优化
在CheckPoint之前可以先cache一下
//设置CheckPoint位置
sc.setCheckpointDir("spark/data/checkpoint")
//读取分数表
val studentRDD: RDD[String] = sc.textFile("spark/data/students.txt")
val stuRDD: RDD[String] = studentRDD.map(line => {
println("map...")//测试用,显示次数即为RDD的运行次数
line
})
/**
* checkpoint : 快照, 将rdd的数据持久化到hdfs, 数据不会丢失
*/
//优化
stuRDD.cache()
stuRDD.checkpoint()
// 统计i班级人数
stuRDD
.map(line => (line.split(",")(4), 1))
.reduceByKey(_ + _)
.foreach(println)
// 统计性别人数
stuRDD
.map(line => (line.split(",")(3), 1))
.reduceByKey(_ + _)
.foreach(println)
stuRDD
.map(line => (line.split(",")(3), 1))
.reduceByKey(_ + _)
.foreach(println)
广播变量
- 当在算子内使用到Driver端的一个变量的时候,这个变量会被封装到task中,变成一个变量副本发送到Executor中
- 由于task的数量一般大于Executor的数量所有,会产生很多的变量副本,会降低任务执行的速度
使用广播变量
- 在Driver端定义一个广播变量
- 在算子中如果使用到广播变量,先去Executor中获取
- 如果Executor中没有这个广播变量,Executor会去Driver端获取广播变量
- 后续的task就可以直接使用
- 使用广播变量之后变量副本 <= Executor数量
val student: RDD[String] = sc.textFile("students.txt")
val list: List[String] = List("文科一班","文科二班","理科一班")
//将集合广播
val broadList: Broadcast[List[String]] = sc.broadcast(list)
val result: RDD[String] = student.filter(line => {
val clazz: String = line.split(",")(4)
//获取广播变量
val value: List[String] = broadList.value
value.contains(clazz)
})
result.foreach(println)
累加器
- 在算子内部修改Driver端的一个变量是不会生效的,因为在算子里面的代码在Executor端运行,算子外面的代码在Driver端运行,属于不同的JVM
- 累加器的使用
- 在Driver定义一个累加器
- 在Executor端进行累加
- 在Driver端读取累加结果
//定义累加器,默认值是0
val acc: LongAccumulator = sc.longAccumulator
rdd.foreach(i => {
acc.add(1)
})
println(acc.value)
//不使用累加器
var a: Int = 0
rdd.foreach(i => {
a+=1
})
println(a)
BlockManager
- 在每一个Executor中都有一个块管理器,用来管理Executor中的数据
- 管理范围
- RDD缓存的数据
- 广播变量和累加器
- shuffle文件
shuffle
- hashshuffle 小文件数 = map数 * reduce数
- hashshuffleManager 小文件数 = core数 * reduce数
- sortshuffle(默认) 小文件数 = 2 * map数
- sortshuffle bypass机制,在溢写的过程中不排序,当reduce的数量小于200的时候会触发
资源调度和任务调度
- 当在代码中出现new SparkContext的时候开始资源调度
- client模式
- Driver在本地启动
- 向RM申请启动AM
- AM启动后向RM申请资源
- RM分配资源启动Executor
- Executor启动之后反向注册给Driver
(AM ApplicationMaster RM ResourceManage)
- cluster模式
- Driver和AM合二为一,功能合并
- AM启动后向RM申请资源
- RM分配资源启动Executor
- Executor启动之后反向注册给Driver
- 任务调度
- 当代码中遇到Action算子的时候开始任务调度
- 构建DAG有向无环图
- DAGScheduler根据宽窄依赖切分stage
- DAGScheduler将stage以taskSet的形式发送给TaskScheduler
- TaskScheduler将task发送到Executor中执行(会尽量将task发送到数据所在的节点执行)
- Executor向TaskScheduler汇报任务执行情况
- task失败之后TaskScheduler重试三次,如果因为shuffle file not found导致的异常,TaskSchedule不负责重试task,而是由DAGScheduler重试上一个stage
- TaskSchedule重试三次还失败,由DAGScheduler重试stage