RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是spark中最基本也是最重要的概念之一。它是spark中一种基本的数据抽象,有容错机制并可以被并行操作的元素集合,具有只读、分区、容错、高效、无需物化、可以缓存、RDD依赖等特征。RDD的知识较为庞杂,这里只能按我了解的做一些简单介绍。
1、partition
一份待处理的原始数据会被按照相应的逻辑(例如jdbc和hdfs的split逻辑)切分成n份,每份数据对应到RDD中的一个Partition,Partition的数量决定了task的数量(一个partition对应一个task),影响着程序的并行度。一个RDD是有n个partition组成。
2、operater算子
算子是spark中对一些数据处理的常用操作的抽象,spark算子分为两类:transform和action
transform算子是一种延迟性操作,也就是把一个RDD转换成另外一个RDD而不是马上执行,不会提交job。常用的有:map,flatmap,join,groupBykey等。
action算子会对RDD 计算出一个结果,并把结果返回到驱动器程序中,或把结果存储到外部存储系统(如 HDFS)中。每有一个action便会提交一个job。常用的有reduce,collect,count,take等。
3、lineage和cache缓存机制
lineage叫做逻辑执行计划,通过DAG(有向无环图)对RDD间关系的建模,描述RDD间的依赖关系,但是我比较喜欢它的另一种说法:血统。在transform操作时会从一个RDD转化为另一个RDD,多个连续的transform算子的话就会多次转化,可以理解成一种进化的过程,一个最原始的RDD经过多个transform算子最终转化为一个较为成熟的RDD,然后通过action算子计算出结果。DAG下面还会重点介绍
但是考虑一个问题,加入我在一系列转化的中间步骤出了错该怎么办,因为spark是基于内存计算的,不可能将出错RDD前面的所有RDD都保存下来,所以就需要重头再来,这样就花费了大量的时间。所以为了解决这个问题,spark采用缓存的机制,也就是通过rdd.persist或者rdd.cache的方法将前面的计算结果缓存,但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供后面重用。
除了上面的原因外,当持久化一个RDD后,每个节点都会把计算的分片的结果保存在内存中,之后可以对此数据集在其他action中再次使用。这使得后续的action变得迅速(通常快10x)
缓存有不同的级别,也可以选择存放在内存或磁盘上,读者可以自己去了解
1、工作原理
RDD算子构建了RDD之间的关系,整个计算过程形成了一个由RDD和关系构成的DAG。
简单理解为点和线构成图。
点是某种数据结构,spark计算框架的点就是RDD(或者stage)
线就是关联关系,spark计算框架的线就是RDD算子(或者宽依赖算子)
spark计算框架中DAG分2种,getShuffleDependencies DAG 和 getMissingParentStages DAG。
getShuffleDependencies :
getMissingParentStages:
仔细看上两张图,图2为图1的一部分。
这2种DAG的关系就是如此,外层的DAG遍历过程如果遇到内层 DAG直接跳入内层检索,整个过程可以看作一个大的DAG,实际作用是检索RDD数据内存位置或磁盘位置的过程。
(插进来两个概念:
宽依赖:父RDD的分区被子RDD的多个分区使用 例如 groupByKey、reduceByKey、sortByKey等操作会产生宽依赖,会产生shuffle,DAG中根据窄依赖划分stage边界。
窄依赖:父RDD的每个分区都只被子RDD的一个分区使用 例如map、filter、union等操作会产生窄依赖)
它们的执行过程是这样的:
1,任务触发是有Action 算子触发(详见上一篇算子),查找的入口就是action算子前的最后一个RDD(finalRDD图中:RDD G),开始遍历DAG。
2,getShuffleDependencies遍历宽依赖关系,遇到窄依赖放入HashMap visit,遇到宽依赖时直接跳转到第3步。如果结束返回完成。
3,getMissingParentStages遍历窄依赖,创建resultstage(方法createResultStage),遇到窄依赖放入HashMap visit,遇到宽依赖递归第2步骤 。如果结束跳入上一步继续检索。
4,遇到可计算的stage (finalStage ),可计算指的是stage中所有RDD都可以寻址到内存或磁盘,提交stage任务到taskscheduler,taskscheduler负责分发任务到集群的worker中计算。如果结束跳入上一步继续检索。
注:2 3 步中分别维护自己的HashMap visit ,遇到宽依赖创建resultstage,为什么宽依赖是划分stage边界的原因。
上面这些操作由DAGSchedule执行
2、DAG的好处
比较hadoop,每执行一个map或reduce都要开启一个新的进程,是不是很耗时,但是在spark中通过划分RDD的依赖关系,可以让多个窄依赖在一个线程中以流水线执行,快速且节省资源。
DAG将一个job根据宽依赖划分多个stage,一个stage的父stage都执行完才能开始执行,通过这样有逻辑地执行任务可以避免冗余操作,合理安排执行顺序,大大节约了时间。