1、RDD概述
1.1 什么是RDD
- RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。
- Dataset:它是一个集合,集合里面有很多个元素
- Distributed:rdd中的数据是进行了分布式存储,后期方便于进行分布式计算。
- Resilient:弹性,意味着rdd的数据可以保存在内存或者是磁盘中。
1.2 RDD的五大属性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emUJjCHH-1634301972674)(基于单词统计来剖析RDD的五大属性.png)]
(1) A list of partitions 一个分区列表 它表示一个rdd中有很多个分区,后期spark任务的计算是以分区为单位进行计算。一个分区就对应上一个task线程。 val rdd=sc.textFile(文件) val rdd=sc.textFile(文件,parNums) 该文件的block个数小于等于2,这个时候rdd的分区数就是2 该文件的block个数大于2, 这个时候rdd的分区数就跟block个数相等 (2) A function for computing each split 作用在每一个分区中的函数 var rdd2=rdd1.map(x=>(x,1)) (3) A list of dependencies on other RDDs 一个RDD会依赖于其他多个RDD,这里就涉及到RDD与RDD之间的依赖关系,后期spark任务的容错机制就是根据这个特性而来。 var rdd2=rdd1.map(x=>(x,1)) (4) Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned) (可选性) 一个分区函数,针对于一个kv类型的RDD才会有分区函数(必须要产生shuffle)。对于不是kv类型的RDD,它的分区函数是None. spark提供了2种shuffle机制, 第一种默认值:hashPartitioner -------> key.hashcode % 分区数=分区号 第二种RangePartitioner: 基于一个范围的分区策略。 (5) Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file) (可选性) 一个优先的数据分区列表,这里就涉及到数据本地性和数据位置最优。 spark后期再进行任务分配的时候,会优先考虑存有数据的worker节点来进行任务的计算。
1.3 创建RDD
- 1、通过一个已经存在的scala集合去构建
- val rdd1=sc.parallelize(List(1,2,3,4),2)
- 2、通过加载外部的数据源去构建
- val rdd2=sc.textFile("/words.txt")
- 3、通过一个已经存在的rdd去构建
- val rdd3=rdd2.flatMap(x=>x.split(" "))
2、RDD中算子操作
rdd中算子一共可以分为2类
transformation(转换)
- 它是一个转换,可以实现把一个rdd转换生成一个新的rdd,它不会立即触发任务的运行,它是延迟加载。
- 它只是记录下作用在rdd的上转换操作
- 比如
- flatMap/map/reduceByKey/sortBy
action (动作)
它会触发任务的真正运行
比如
- collect/saveAsTextFile
3、RDD的依赖关系
rdd与rdd之间有依赖关系
- 窄依赖
- 窄依赖指的是每一个父RDD的Partition最多被子RDD的一个Partition使用
总结:窄依赖我们形象的比喻为独生子女- 窄依赖不会产生shuffle
- 宽依赖
- 宽依赖指的是多个子RDD的Partition会依赖同一个父RDD的Partition
总结:宽依赖我们形象的比喻为超生- 宽依赖会产生shuffle
4、lineage(血统)
- rdd后期会进行大量的转换操作,我们把rdd的这些操作行为记录下来,记录下来的信息我们就称为lineage(血统)
- 血统好处
- 当前某一个rdd的分区数据丢失了,可以通过血统这一层关系来重新计算恢复得到。
- 这里spark的任务容错机制就是根据血统而来。
5、RDD的缓存机制
5.1 RDD的缓存是什么
- 可以把一个rdd的结果数据进行缓存,后续有其他的job需要依赖于前面rdd的结果数据,这个时候可以直接从缓存中获取得到,避免重复计算。
5.2 如何设置缓存
- rdd中提供了设置缓存的2种方式
- cache:默认是将数据缓存在内存中,其本质是调用persist方法
- persist:可以把数据缓存内存或者是磁盘中,它里面可以设置丰富的缓存级别,这些缓存级别都封装在一个object 中,这个object的名称Storagelevel
- 以上这个2个方法并不是调用之后就立即执行,后续是需要一个action操作,才会触发缓存真正执行。
5.3 如何清除缓存
- 手动清除
- 调用rdd的unpersist 清除缓存数据:rdd1.unpersist(true)
- 自动清除:对于程序来说,如果我们设置了缓存,后期程序结束了,它会自动清除
5.4 什么时候设置缓存
- 1、一个rdd后期被使用了多次
- val rdd2=rdd1.flatMap(_.split(" "))
- val rdd3=rdd1.map(x=>(x,1))
- 上面的rdd1被使用了多次,每一次使用都需要先把rdd1的结果数据先计算一下,这个时候就可以对rdd1设置缓存,避免后续的rdd需要前面的结果。
- 2、某一个rdd的数据来之不易 是经过多个转换而来
- val rdd2=rdd1.flatMap(_.split(" ")).map().xxxxx.xxxxxxx.xxxxxxx.xxxxxxx.xxx.xxx
6、DAG有向无环图和划分stage
6.1 什么是DAG
DAG就是按照rdd的一系列操作最后生成了一个有方向无闭环的图,这个图我们就称为DAG有向无环图。
按照操作逻辑划分成不同的stage(不同的调度阶段)
7、spark任务调度
(1)Driver会运行客户端main方法中的代码,代码就会构建SparkContext对象,在构建SparkContext对象中,会创建DAGScheduler和TaskScheduler,然后按照rdd一系列的操作生成DAG有向无环图。最后把DAG有向无环图提交给DAGScheduler。 (2)DAGScheduler拿到DAG有向无环图后,按照宽依赖进行stage的划分,这个时候会产生很多个stage,每一个stage中都有很多可以并行运行的task,把每一个stage中这些task封装在一个taskSet集合中,最后提交给TaskScheduler。 (3)TaskScheduler拿到taskSet集合后,依次遍历每一个task,最后提交给worker节点的exectuor进程中。task就以线程的方式运行在worker节点的executor进程中。
8、spark的容错机制之checkpoint
什么是checkpoint
- cache
- 默认是把数据缓存在内存中,后续操作起来速度比较快
- 但是由于进程或者是服务器挂掉了,这个时候内存中的数据肯定是丢失,也就是说cache不是非常安全,数据丢失的概率比较大。
- persist
- 有丰富的缓存级别,可以把数据缓存在磁盘中,然后需要用到该数据,可以进行磁盘io操作获取得到,这一点比cache速度会慢点,但是比cache安全点
- 这里同样也有数据丢失的可能性(磁盘损坏、系统管理员由于误操作把本地数据清除掉了)
- checkpoint
- 它是提供了一个相对而言更加可靠的持久化数据的方式
- 它可以把rdd的数据写入到分布式文件系统(HDFS)去保存,利用了hdfs高可靠,多个副本机制最大程度保证数据不丢失。
区别
- cache和persist:
- 这2个方法都可以将rdd的数据进行缓存,后续都要有一个action操作,才会触发缓存任务的执行
- 它不会改变rdd的血统。整个程序结束之后,这些缓存数据自己被清除了。
- checkpoint:
- 可以把数据持久化到hdfs上,这个时候先rdd.checkpoint操作,然后也需要一个action。
- 一个action操作就是一个job,在这里首先它会执行action这个job,执行完成之后,它会开启一个新的job来执行checkpoint操作,写入之前设置的文件系统持久化, 也就是说在这里比cache和persist多了一个job。它会改变rdd的血统。checkpoint 执行完后,rdd 已经没有依赖 RDD,只有一个checkpointRDD,checkpoint 之后,RDD的lineage就改变了
如何使用checkpoint
- 1、通过sparkContext对象设置checkpoint目录,用于保存rdd的数据
- sc.setCheckpointDir("/ckDir")
- 2、对需要持久化的rdd调用一个方法checkpoint方法
- val rdd1=sc.textFile("/words.txt")
- rdd1.checkpoint
- 3、后续需要有个action操作,触发checkpoint的执行
- rdd1.collect
数据丢失之后的恢复顺序
- 首先看一写有没有设置cache,如果有,直接从cache获取得到
- 如果没有cache,看一下有没有做checkpoint,如果有就直接从checkpoint获取得到
- 如果没有checkpoint,利用血统这层关系来重新计算恢复得到。