一、简介版
(1)A list of partitions
一组分区:RDD由很多partition构成,有多少partition就对应有多少task
(2)A function for computing each split
一个函数:对RDD做计算,相当于对RDD的每个split或partition做计算
(3)A list of dependencies on other RDDs
RDD之间有依赖关系,可溯源
(4)Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
一个Partitioner:即RDD的分片函数,如果RDD里面存的数据是key-value形式,则可以传递一个自定义的Partitioner进行重新分区
(5)Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)
一个列表:存储存取每个Partition的优先位置(preferred location),计算每个split时,在split所在机器的本地上运行task是最好的,避免了数据的移动,split有多个副本,所以preferred location不止一个
二、详细版
(1)分区列表( a list of partitions)
Spark RDD是被分区的,每一个分区都会被一个计算任务(Task)处理,分区数决定了并行计算的数量,RDD的并行度默认从父RDD传给子RDD。默认情况下,一个HDFS上的数据分片就是一个 partiton,RDD分片数决定了并行计算的力度,可以在创建RDD时指定RDD分片个数(分区)。
如果不指定分区数量,当RDD从集合创建时,则默认分区数量为该程序所分配到的资源的CPU核数(每个Core可以承载2~4个 partition),如果是从HDFS文件创建,默认为文件的 Block数。
(2)每一个分区都有一个计算函数( a function for computing each split)
每个分区都会有计算函数, Spark的RDD的计算函数是以分片
为基本单位的,每个RDD都会实现 compute函数,对具体的分片进行计算,RDD中的分片是并行的,所以是分布式并行计算。
有一点非常重要,就是由于RDD有前后依赖关系,遇到宽依赖关系,如reduce By Key等这些操作时划分成 Stage, Stage内部的操作都是通过 Pipeline进行的,在具体处理数据时它会通过 Blockmanager来获取相关的数据,因为具体的 split要从外界读数据,也要把具体的计算结果写入外界,所以用了一个管理器,具体的 split都会映射成 BlockManager的Block,而具体的split会被函数处理,函数处理的具体形式是以任务的形式进行的。
(3)依赖关系( a list of dependencies on other RDDS)
由于RDD每次转换都会生成新的RDD,所以RDD会形成类似流水线一样的前后依赖关系,当然宽依赖就不类似于流水线了,宽依赖后面的RDD具体的数据分片会依赖前面所有的RDD的所有数据分片,这个时候数据分片就不进行内存中的 Pipeline,一般都是跨机器的,因为有前后的依赖关系,所以当有分区的数据丢失时, Spark会通过依赖关系进行重新计算,从而计算出丢失的数据,而不是对RDD所有的分区进行重新计算。
RDD之间的依赖有两种:窄依赖( Narrow Dependency)
和宽依赖( Wide Dependency)
。
RDD是 Spark的核心数据结构
,通过RDD的依赖关系形成调度关系。通过对RDD的操作形成整个 Spark程序。
窄依赖指的是每一个 parent RDD的 Partition最多被 child rdd的一个 Partition所使用。
宽依赖指的是多个 child RDD的Partion会依赖于同一个 parent RDD的 Partition。
可以从两个方面来理解RDD之间的依赖关系,一方面是RDD的 parent RDD是什么(上图左边的RDD),另一方面是依赖于 parent RDD的哪些 Partion;根据依赖于 parent RDD的哪些 Partion的不同情况, Spark将 Dependency分为宽依赖和窄依赖两种。
Spak中的宽依赖指的是生成的RDD的每一个 partition都依赖于父RDD所有的 partition。
宽依赖典型的操作有 group by key, sort by key等,宽依赖意味着Shuffle操作,这是 Spark划分Stage的边界的依据。
Spark中的宽依赖支持两种 Shuffle Manager,即 Hash Shufflemanager和 Sort shufflemanager,前者是基于Hash的 Shuffle机制,后者是基于排序的 Shuffle机制。
(4)key- value数据类型的RDD分区器( a Partitioner for key- alue RDDS)、控制分区策略和分区数
每个key- value形式的RDD都有 Partitioner属性,它决定了RDD如何分区。当然,Partiton的个数还决定了每个Stage的Task个数。
RDD的分片函数可以分区( Partitioner),可传入相关的参数,如 Hash Partitioner和 Range Partitioner,它本身针对key- value的形式,如果不是key-value的形式它就不会有具体的 Partitioner, Partitioner本身决定了下一步会产生多少并行的分片,同时它本身也决定了当前并行( Parallelize) Shuffle输出的并行数据,从而使Spark具有能够控制数据在不同结点上分区的特性,用户可以自定义分区策略,如Hash分区等。
spark提供了 partition By运算符,能通过集群对RDD进行数据再分配来创建一个新的RDD。
(5)每个分区都有一个优先位置列表,即首选位置( a list of preferred locations to compute each split on)
优先位置列表会存储每个 Partition的优先位置,对于一个HDFS文件来说,就是每个Partition块的位置。
观察运行 Spark集群的控制台就会发现,Spark在具体计算、具体分片以前,它已经清楚地知道任务发生在哪个结点上,也就是说任务本身是计算层面的、代码层面的,代码发生运算之前它就已经知道它要运算的数据在什么地方,有具体结点的信息。
这就符合大数据中数据不动代码动的原则,即“移动数据不如移动计算
”的理念。数据不动代码动的最高境界是数据就在当前结点的内存中。这时候有可能是 Memory级别或 Tachyon级别的,Spark本身在进行任务调度时会尽可能地将任务分配到处理数据的数据块所在的具体位置。