Apache Spark RDD初談3

來源:互聯網
上載者:User

標籤:

    RDD的轉換和DAG的產生

      Spark會根據使用者提交的計算邏輯中的RDD的轉換和動作來產生RDD之間的依賴關係,同時這個計算鏈也就產生了邏輯上的DAG。接下來以“Word Count”為例,詳細描述這個DAG產生的實現過程。

   Spark Scala版本的Word Count程式如下:

1: val file = spark.textFile("hdfs://...")2: val counts = file.flatMap(line => line.split(" "))3:  .map(word => (word, 1))4:  .reduceByKey(_ + _)5: counts.saveAsTextFile("hdfs://...")

     file和counts都是RDD,其中file是從HDFS上讀取檔案並建立了RDD,而counts是在file的基礎上通過flatMap、map和reduceByKey這三個RDD轉換產生的。最後,counts調用了動作saveAsTextFile,使用者的計算邏輯就從這裡開始提交的叢集進行計算。那麼上面這5行代碼的具體實現是什麼呢?

     1)行1:spark是org.apache.spark.SparkContext的執行個體,它是使用者程式和Spark的互動介面。spark會負責串連到叢集管理者,並根據使用者佈建或者系統預設設定來申請計算資源,完成RDD的建立等。

spark.textFile("hdfs://...")就完成了一個org.apache.spark.rdd.HadoopRDD的建立,並且完成了一次RDD的轉換:通過map轉換到一個org.apache.spark.rdd.MapPartitions-RDD。

也就是說,file實際上是一個MapPartitionsRDD,它儲存了檔案的所有行的資料內容。

   2)行2:將file中的所有行的內容,以空格分隔為單詞的列表,然後將這個按照行構成的單字清單合并為一個列表。最後,以每個單詞為元素的列表被儲存到MapPartitionsRDD。

    3)行3:將第2步產生的MapPartitionsRDD再次經過map將每個單詞word轉為(word, 1)的元組。這些元組最終被放到一個MapPartitionsRDD中。

    4)行4:首先會產生一個MapPartitionsRDD,起到map端combiner的作用;然後會產生一個ShuffledRDD,它從上一個RDD的輸出讀取資料,作為reducer的開始;最後,還會產生一個MapPartitionsRDD,起到reducer端reduce的作用。

    5)行5:首先會產生一個MapPartitionsRDD,這個RDD會通過調用org.apache.spark.rdd.PairRDDFunctions#saveAsHadoopDataset向HDFS輸出RDD的資料內容。最後,調用org.apache.spark.SparkContext#runJob向叢集提交這個計算任務。

    RDD之間的關係可以從兩個維度來理解:一個是RDD是從哪些RDD轉換而來,也就是RDD的parent RDD(s)是什麼;還有就是依賴於parent RDD(s)的哪些Partition(s)。這個關係,就是RDD之間的依賴,org.apache.spark.Dependency。根據依賴於parent RDD(s)的Partitions的不同情況,Spark將這種依賴分為兩種,一種是寬依賴,一種是窄依賴。

    

    RDD的依賴關係  

     RDD和它依賴的parent RDD(s)的關係有兩種不同的類型,即窄依賴(narrow dependency)和寬依賴(wide dependency)。

     1)窄依賴指的是每一個parent RDD的Partition最多被子RDD的一個Partition使用,1所示。

     2)寬依賴指的是多個子RDD的Partition會依賴同一個parent RDD的Partition,2所示。

      

        圖 1  RDD的窄依賴

     
                 圖 2 RDD的寬依賴

 

     接下來可以從不同類型的轉換來進一步理解RDD的窄依賴和寬依賴的區別,3所示。

     對於map和filter形式的轉換來說,它們只是將Partition的資料根據轉換的規則進行轉化,並不涉及其他的處理,可以簡單地認為只是將資料從一個形式轉換到另一個形式。對於union,只是將多個RDD合并成一個,parent RDD的Partition(s)不會有任何的變化,可以認為只是把parent RDD的Partition(s)簡單進行複製與合并。對於join,如果每個Partition僅僅和已知的、特定的Partition進行join,那麼這個依賴關係也是窄依賴。對於這種有規則的資料的join,並不會引入昂貴的Shuffle。對於窄依賴,由於RDD每個Partition依賴固定數量的parent RDD(s)的Partition(s),因此可以通過一個計算任務來處理這些Partition,並且這些Partition相互獨立,這些計算任務也就可以並存執行了。

對於groupByKey,子RDD的所有Partition(s)會依賴於parent RDD的所有Partition(s),子RDD的Partition是parent RDD的所有Partition Shuffle的結果,因此這兩個RDD是不能通過一個計算任務來完成的。同樣,對於需要parent RDD的所有Partition進行join的轉換,也是需要Shuffle,這類join的依賴就是寬依賴而不是前面提到的窄依賴了。

 

*******************************************************

所有的依賴都要實現trait Dependency[T]:

abstract class Dependency[T] extends Serializable {      def rdd: RDD[T] }  其中rdd就是依賴的parent RDD。  對於窄依賴的實現是:  abstract class NarrowDependency[T](_rdd: RDD[T]) extends Dependency[T] {      //返回子RDD的partitionId依賴的所有的parent RDD的Partition(s)      def getParents(partitionId: Int): Seq[Int]      override def rdd: RDD[T] = _rdd }  現在有兩種窄依賴的具體實現,一種是一對一的依賴,即OneToOneDependency:  class OneToOneDependency[T](rdd: RDD[T]) extends NarrowDependency[T](rdd) {      override def getParents(partitionId: Int) = List(partitionId) *******************************************************  *******************************************************    通過getParents的實現不難看出,RDD僅僅依賴於parent RDD相同ID的Partition。
還有一個是範圍的依賴,即RangeDependency,它僅僅被org.apache.spark.rdd.UnionRDD使用。UnionRDD是把多個RDD合成一個RDD,這些RDD是被拼接而成,即每個parent RDD的Partition的相對順序不會變,只不過每個parent RDD在UnionRDD中的Partition的起始位置不同。因此它的getPartents如下: override def getParents(partitionId: Int) = {      if (partitionId >= outStart && partitionId < outStart + length) {         List(partitionId - outStart + inStart)      } else {         Nil      } }*******************************************************  *******************************************************

其中,inStart是parent RDD中Partition的起始位置,outStart是在UnionRDD中的起始位置,length就是parent RDD中Partition的數量。

寬依賴的實現只有一種:ShuffleDependency。子RDD依賴於parent RDD的所有Partition,因此需要Shuffle過程:

class ShuffleDependency[K, V, C](      @transient _rdd: RDD[_ <: Product2[K, V]],      val partitioner: Partitioner,      val serializer: Option[Serializer] = None,      val keyOrdering: Option[Ordering[K]] = None,      val aggregator: Option[Aggregator[K, V, C]] = None,      val mapSideCombine: Boolean = false ) extends Dependency[Product2[K, V]] {  override def rdd = _rdd.asInstanceOf[RDD[Product2[K, V]]] //擷取新的shuffleId val shuffleId: Int = _rdd.context.newShuffleId() //向ShuffleManager註冊Shuffle的資訊 val shuffleHandle: ShuffleHandle = _rdd.context.env.shuffleManager.registerShuffle(      shuffleId, _rdd.partitions.size, this )       _rdd.sparkContext.cleaner.foreach(_.registerShuffleForCleanup( this )) }寬依賴支援兩種Shuffle Manager,即org.apache.spark.shuffle.hash.HashShuffleManager(基於Hash的Shuffle機制)和org.apache.spark.shuffle.sort.SortShuffleManager(基於排序的Shuffle機制)。 *******************************************************

 

   DAG的產生

      原始的RDD(s)通過一系列轉換就形成了DAG。RDD之間的依賴關係,包含了RDD由哪些Parent RDD(s)轉換而來和它依賴parent RDD(s)的哪些Partitions,是DAG的重要屬性。藉助這些依賴關係,DAG可以認為這些RDD之間形成了Lineage(血統)。藉助Lineage,能保證一個RDD被計算前,它所依賴的parent RDD都已經完成了計算;同時也實現了RDD的容錯性,即如果一個RDD的部分或者全部的計算結果丟失了,那麼就需要重新計算這部分丟失的資料。

      那麼Spark是如何根據DAG來產生計算任務呢?首先,根據依賴關係的不同將DAG劃分為不同的階段(Stage)。對於窄依賴,由於Partition依賴關係的確定性,Partition的轉換處理就可以在同一個線程裡完成,窄依賴被Spark劃分到同一個執行階段;對於寬依賴,由於Shuffle的存在,只能在parent RDD(s) Shuffle處理完成後,才能開始接下來的計算,因此寬依賴就是Spark劃分Stage的依據,即Spark根據寬依賴將DAG劃分為不同的Stage。在一個Stage內部,每個Partition都會被分配一個計算任務(Task),這些Task是可以並存執行的。Stage之間根據依賴關係變成了一個大粒度的DAG,這個DAG的執行順序也是從前向後的。也就是說,Stage只有在它沒有parent Stage或者parent Stage都已經執行完成後,才可以執行。

Apache Spark RDD初談3

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.