1.分區列表(a list of partitions)。Spark RDD是被分區的,每一個分區都會被一個計算任務(Task)處理,分區數決定並行計算數量,RDD的並行度默認從父RDD傳給子RDD。默認情況下,一個HDFS上的數據分片就是一個Partition,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等寬依賴操作的算子,Spark將根據寬依賴划分Stage,Stage內部通過Pipeline操作,通過Block Manager獲取相關的數據,因為具體的split要從外界讀數據,也要把具體的計算結果寫入外界,所以用了一個管理器,具體的split都會映射成BlockManager的Block,而具體split會被函數處理,函數處理的具體形式是以任務的形式進行的。
3.依賴於其他RDD的列表(a list of dependencies on other RDDs)。RDD的依賴關系,由於RDD每次轉換都會生成新的RDD,所以RDD會形成類似流水線的前后依賴關系,當然,寬依賴就不類似於流水線了,寬依賴后面的RDD具體的數據分片會依賴前面所有的RDD的所有的數據分片,這時數據分片就不進行內存中的Pipeline,這時一般是跨機器的。因為有前后的依賴關系,所以當有分區數據丟失的時候,Spark會通過依賴關系重新計算,算出丟失的數據,而不是對RDD所有的分區進行重新計算。RDD之間的依賴有兩種:窄依賴(Narrow Dependency)、寬依賴(Wide Dependency)。RDD是Spark的核心數據結構,通過RDD的依賴關系形成調度關系。通過對RDD的操作形成整個Spark程序。
RDD有Narrow Dependency和Wide Dependency兩種不同類型的依賴,其中的Narrow Dependency指的是每一個parent RDD的Partition最多被child RDD的一個Partition所使用,而Wide Dependency指的是多個child RDD的Partition會依賴於同一個parent RDD的Partition。可以從兩個方面來理解RDD之間的依賴關系:一方面是該RDD的parent RDD是什么;另一方面是依賴於parent RDD的哪些Partitions;根據依賴於parent RDD的Partitions的不同情況,Spark將Dependency分為寬依賴和窄依賴兩種。Spark中寬依賴指的是生成的RDD的每一個partition都依賴於父RDD的所有partition,寬依賴典型的操作有groupByKey、sortByKey等,寬依賴意味着shuffle操作,這是Spark划分Stage邊界的依據,Spark中寬依賴支持兩種Shuffle Manager,即HashShuffleManager和SortShuffleManager,前者是基於Hash的Shuffle機制,后者是基於排序的Shuffle機制。Spark 2.2現在的版本中已經沒有Hash Shuffle的方式。
4.key-value數據類型的RDD分區器(-Optionally,a Partitioner for key-value RDDS),控制分區策略和分區數。每個key-value形式的RDD都有Partitioner屬性,它決定了RDD如何分區。當然,Partition的個數還決定每個Stage的Task個數。RDD的分片函數,想控制RDD的分片函數的時候可以分區(Partitioner)傳入相關的參數,如HashPartitioner、RangePartitioner,它本身針對key-value的形式,如果不是key-value的形式,它就不會有具體的Partitioner。Partitioner本身決定了下一步會產生多少並行的分片,同時,它本身也決定了當前並行(parallelize)Shuffle輸出的並行數據,從而使Spark具有能夠控制數據在不同節點上分區的特性,用戶可以自定義分區策略,如Hash分區等。Spark提供了“partitionBy”運算符,能通過集群對RDD進行數據再分配來創建一個新的RDD。
5.每個分區都有一個優先位置列表(-Optionally,a list of preferred locations to compute each split on)。它會存儲每個Partition的優先位置,對於一個HDFS文件來說,就是每個Partition塊的位置。觀察運行spark集群的控制台會發現Spark的具體計算,具體分片前,它已經清楚地知道任務發生在什么節點上,也就是說,任務本身是計算層面的、代碼層面的,代碼發生運算之前已經知道它要運算的數據在什么地方,有具體節點的信息。這就符合大數據中數據不動代碼動的特點。數據不動代碼動的最高境界是數據就在當前節點的內存中。這時有可能是memory級別或Alluxio級別的,Spark本身在進行任務調度時候,會盡可能將任務分配到處理數據的數據塊所在的具體位置。據Spark的RDD.Scala源碼函數getPreferredLocations可知,每次計算都符合完美的數據本地性。
