Spark学习(三):核心模块之RDD详解
1. Spark核心模块整体简介
(1)Spark的关键运算组件图:
(2)总结:
Spark的核心组件总结包括RDD、Scheduler、Storage、Shuffle四部分:
1)RDD是Spark最核心最精髓的部分,spark将所有数据都抽象成RDD。
2)Scheduler是Spark的调度机制,分为DAGScheduler和TaskScheduler。
3)Storage模块主要管理缓存后的RDD、shuffle中间结果数据和broadcast数据
4)Shuffle分为Hash方式和Sort方式,两种方式的shuffle中间数据都写本地盘
2. 核心模块——RDD整体介绍
(1)RDD的概念:
1)RDD是Spark的基石,也是Spark的灵魂。
2)RDD是弹性分布式数据集,是只读的分区记录集合。
(2)每个RDD有5个主要的属性:
1)一组分片(Partition):数据集的最基本组成单位
2)一个计算每个分片的函数:对于给定的数据集,需要做哪些计算
3)依赖(Dependencies):RDD的依赖关系,描述了RDD之间的lineage
4)preferredLocations(可选):对于data partition的位置偏好
5)partitioner(可选) -- 对于计算出来的数据结果如何分发
(3)RDD的举例1:
rdd1= sparkContext.textFile( “hdfs://…”)
rdd1是一个MappedRDD,该RDD是从外部文件创建的。
可以传入分片个数参数,否则采用defaultMinPartitions。
(4)RDD的举例2:
rdd2= rdd1.filter(_.startsWith( “ERROR”))
rdd2也是一个FilteredRDD,是从rdd1这个RDD衍生(即计算)得到的。
rdd1是rdd2的父节点,即rdd2依赖rdd1。
filter是RDD的操作,即每个分片需要计算的函数。
3. 核心模块——RDD操作
(1)RDD的操作工分为两类:转换transformations和动作actions
1)转换transformations
从现有的数据集创建一个新的数据集即数据集中的内容会发生更改,由数据集A转换成为数据集B
2)动作actions
在数据集上运行计算后,返回一个值给驱动程序。 即数据集中的内容会被归约为一个具体的数值(Scala标量、集合类型的数据或存储)。
(2)具体的RDD方法举例如图:
4. 核心模块——RDD的持久化
(1)RDD持久化的时机:
1)默认情况下,每一个转换过的RDD都会在它之上执行一个动作时被重新计算。
2)如果rdd只被使用一次或者很少次,不需要持久化。如果rdd被重复使用或者计算其代价很高,才考虑持久化。另外,shuffle后生成的rdd尽量持久化,因为shuffle代价太高。
(2)RDD持久化的效果:
1RDD被缓存后,Spark将会在集群中,保存相关元数据,下次查询这个RDD时,它将能更快速访问,不需要计算。
2)如果持久化无谓的RDD,会浪费内存(或硬盘)空间,反而降低系统整体性能
(3)RDD持久化的方法:
1)使用 persist方法(或者cache方法),持久化一个RDD在内存或磁盘中。
2)cache()过程是将RDD persist在内存里,persist()操作可以为RDD指定StorageLevel。
(4)RDD的存储级别StorageLevel
5. 核心模块——RDD依赖关系
(1)RDD之间存在依赖的原因:
1)RDD只能基于在稳定物理存储中的数据集和其他已有的RDD上执行确定性操作来创建。
2)能从其他RDD通过确定操作创建新的RDD的原因是RDD含有从其他RDD衍生(即计算)出本RDD的相关信息(即Lineage)
(2)RDD依赖的分类:
Dependency代表了RDD之间的依赖关系,即血缘(Lineage),分为窄依赖和宽依赖。
(3)窄依赖和宽依赖图形表示:
(4)依赖详解
窄依赖:
•一个父RDD最多被一个子RDD用
•常见的操作:map、filter、union等;
宽依赖:
•指子RDD的分区依赖于父RDD的所有分区,这是因为shuffle类操作要求所有父分区可用。
•常见的操作:比如groupByKey、reduceByKey、 sort、partitionBy等;
(5)基于RDD的依赖关系,spark的任务阶段划分原则:
根据RDD依赖关系的不同,Spark将每一个job分为不同的stage,stage之间的依赖关系形成了DAG图。具体来讲就是:
1)窄依赖Spark将其尽量划分在同一个stage中,因为它们可以进行流水线计算。
2)宽依赖往往意味着shuffle操作,这也是Spark划分stage的主要边界。
(6)阶段stage划分的示例图:
(7)针对示例图的解释:
1)一个Stage的开始就是从外部存储或者shuffle结果中读取数据;一个Stage的结束就是发生shuffle或者生成结果时。
2)由于rdd F是rdd G的宽依赖,所以将F与G分别划分到不同的stage,但是B是G的窄依赖(B的每个分区只被使用一次),所以B与G还保持在一个stage。
3)由于rdd A是rdd B的宽依赖,所以A和B划分在不同的stage
4)在图中Stage2中,从map到union都是窄依赖,在一个stage,这两步操作可以形成一个流水线操作,通过map操作生成的partition可以不用等待整个RDD计算结束,而是继续进行union操作,这样大大提高了计算的效率。
6. 核心模块——RDD的容错关系
如果transformation操作中间发生计算失败:
1)运算是窄依赖
只要把丢失的父RDD分区重算即可,跟其他节点没有依赖,这样可以大大加快场景恢复的开销。
2)运算是宽依赖
需要父RDD的所有分区都存在, 重算代价就较高
3)增加检查点
当Lineage特别长时或者有宽依赖时,主动调用 checkpoint把当前数据写入稳定存储,作为检查点