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有前后依赖关系,遇到宽依赖关系,如reduceByKey等这些操作时划分成Stage,Stage内部的操作都是通过Pipeline进行的,在具体处理数据时它会通过BlockManager来获取相关的数据,因为具体的split要从外界读数据,也要把具体的计算结果写入外界,所以用了一个管理器,具体的split都会映射成Block-Manager的Block,而具体的split会被函数处理,函数处理的具体形式是以任务的形式进行的。
3.依赖于其他RDD的列表(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程序。
RDD有窄依赖和宽依赖两种不同类型的依赖,其中的窄依赖指的是每一个parent RDD的Partition最多被child RDD的一个Partition所使用,而宽依赖指的是多个child RDD的Par-tition会依赖于同一个parent RDD的Partition。可以从两个方面来理解RDD之间的依赖关系,一方面是RDD的parent RDD是什么,另一方面是依赖于parent RDD的哪些Partion;根据依赖于parent RDD的哪些Partion的不同情况,Spark将Dependency分为宽依赖和窄依赖两种。Spark中的宽依赖指的是生成的RDD的每一个partition都依赖于父RDD所有的partition,宽依赖典型的操作有groupByKey、sortByKey等,宽依赖意味着Shuffle操作,这是Spark划分Stage的边界的依据,Spark中的宽依赖支持两种Shuffle Manager,即HashShuffleMan-ager和SortShuffleManager,前者是基于Hash的Shuffle机制,后者是基于排序的Shuffle机制。(www.xing528.com)
4.key-value数据类型的RDD分区器(a Partitioner for key-value RDDS)、控制分区策略和分区数
每个key-value形式的RDD都有Partitoner属性,它决定了RDD如何分区。当然,Par-titon的个数还决定了每个Stage的Task个数。RDD的分片函数可以分区(Partitioner),可传入相关的参数,如HashPartitioner和RangePartitioner,它本身针对key-value的形式,如果不是key-value的形式它就不会有具体的Partitioner,Partitioner本身决定了下一步会产生多少并行的分片,同时它本身也决定了当前并行(Parallelize)Shuflle输出的并行数据,从而使Spark具有能够控制数据在不同结点上分区的特性,用户可以自定义分区策略,如Hash分区等。Spark提供了partitionBy运算符,能通过集群对RDD进行数据再分配来创建一个新的RDD。
5.每个分区都有一个优先位置列表(a list of preferred locations to compute eachsplit on)
优先位置列表会存储每个Partition的优先位置,对于一个HDFS文件来说,就是每个Partition块的位置。观察运行Spark集群的控制台就会发现,Spark在具体计算、具体分片以前,它已经清楚地知道任务发生在哪个结点上,也就是说任务本身是计算层面的、代码层面的,代码发生运算之前它就已经知道它要运算的数据在什么地方,有具体结点的信息。这就符合大数据中数据不动代码动的原则。数据不动代码动的最高境界是数据就在当前结点的内存中。这时候有可能是Memory级别或Tachyon级别的,Spark本身在进行任务调度时会尽可能地将任务分配到处理数据的数据块所在的具体位置。据Spark的RDD.Scala源代码函数getParferredLocations可知,每次计算都符合完美的数据本地性。
可在RDD类源代码文件中找到4个方法和1个属性,对应上述所阐述的RDD的五大特性,源代码剪辑如下。
在此需要对TaskContext、Partitioner和Partition等概念做出解释,TaskContext是读取或改变执行任务的环境,用org.apache.spark.TaskContext.get()可返回当前可用的TaskContext,可以调用内部的函数访问正在运行任务的环境信息。Partitioner是一个对象,定义了如何在key-value类型的RDD的元素中用key分区,从0到numPartitions-1区间内映射每一个key到partition ID。Partition是在一个RDD的分区标识符,源代码如下。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。