通過分區(Partitioning)提高Spark的運行效能

來源:互聯網
上載者:User

標籤:

  在Sortable公司,很多資料處理的工作都是使用Spark完成的。在使用Spark的過程中他們發現了一個能夠提高Sparkjob效能的一個技巧,也就是修改資料的分區數,本文將舉個例子並詳細地介紹如何做到的。

尋找質數

  比如我們需要從2到2000000之間尋找所有的質數。我們很自然地會想到先找到所有的非質數,剩下的所有數字就是我們要找的質數。

  我們首先遍曆2到2000000之間的每個數,然後找到這些數的所有小於或等於2000000的倍數,在計算的結果中可能會有許多重複的資料(比如6同時是2和3的倍數)但是這並沒有啥影響。

我們在Spark shell中計算:

Welcome to      ____              __     / __/__  ___ _____/ /__    _\ \/ _ \/ _ `/ __/  ‘_/   /___/ .__/\_,_/_/ /_/\_\   version 1.6.1      /_/ Using Scala version 2.10.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45)Type in expressions to have them evaluated.Type :help for more information.Spark context available as sc.SQL context available as sqlContext. scala> val n = 2000000n: Int = 2000000 scala> val composite = sc.parallelize(2 to n, 8).map(x => (x, (2 to (n / x)))).flatMap(kv => kv._2.map(_ * kv._1))composite: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[2] at flatMap at <console>:29 scala>  scala> val prime = sc.parallelize(2 to n, 8).subtract(composite)prime: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[7] at subtract at &lt;console&gt;:31 scala> prime.collect()res0: Array[Int] = Array(563249, 17, 281609, 840761, 1126513, 1958993, 840713, 1959017, 41, 281641, 1681513, 1126441, 73, 1126457, 89, 840817, 97, 1408009, 113, 137, 1408241, 563377, 1126649, 281737, 281777, 840841, 1408217, 1681649, 281761, 1408201, 1959161, 1408177, 840929, 563449, 1126561, 193, 1126577, 1126537, 1959073, 563417, 233, 281849, 1126553, 563401, 281833, 241, 563489, 281, 281857, 257, 1959241, 313, 841081, 337, 1408289, 563561, 281921, 353, 1681721, 409, 281993, 401, 1126897, 282001, 1126889, 1959361, 1681873, 563593, 433, 841097, 1959401, 1408417, 1959313, 1681817, 457, 841193, 449, 563657, 282089, 282097, 1408409, 1408601, 1959521, 1682017, 841241, 1408577, 569, 1408633, 521, 841273, 1127033, 841289,617, 1408529, 1959457, 563777, 841297, 1959473, 577, 593, 563809, 601,...

  答案看起來是可靠的,但是我們來看看這個程式的效能。如果我們到Spark UI裡面看的話可以發現Spark在整個計算過程中使用了3個stages,就是UI中這個計算過程的DAG(Directed Acyclic Graph)可視化圖,其中展示了DAG圖中不同的RDD計算。

在Spark中,只要job需要在分區之間進行資料互動,那麼一個新的stage將會產生(如果使用Spark術語的話,分區之間的資料互動其實就是shuffle)。Spark stage中每個分區將會起一個task進行計算,而這些task負責將這個RDD分區的資料轉化(transform)成另外一個RDD分區的資料。我們簡單地看下Stage 0的task運行情況:

中我們對DurationShuffle Write Size / Records兩列非常感興趣。sc.parallelize(2 to n, 8)已經產生了1999999 records,而這寫記錄均勻地分布到8個分區裡面;每個task的計算幾乎花費了相同的時間,所以這個stage是沒問題的。

  Stage 1是比較重要的stage,因為它運行了mapflatMap transformation,我們來看看它的運行情況:

從可以看出,這個stage啟動並執行並不好,因為工作負載並沒有均衡到所有的task中!93%的資料集中在一個task中,而這個task的計算花費了14s;另外一個比較慢的task花費了1s。然而我們提供了8個core用於計算,而其中的7個core在這13s內都在等待這個stage的完成。這對資源的利用非常不高效。


如果想及時瞭解Spark、Hadoop或者Hbase相關的文章,歡迎關注公用帳號: iteblog_hadoop為什麼會出現這種情況?

  當我們運行sc.parallelize(2 to n, 8)語句的時候,Spark使用分區機制將資料很好地分成8個組。它最有可能使用的是range partitioner,也就是說2-250000被分到第一個分區; 250001-500000分到第二個分區等等。然而我們的map函數將這些數轉成(key,value)pairs,而value裡面的資料大小變化很大(key比較小的時候,value的值就比較多,從而也比較大)。每個value都是一個list,裡面存放著我們需要乘上key並小於2000000的倍數值,有一半以上的索引值對(所有key大於1000000)的value是空的;而key等於2對應的value是最多的,包含了所有從2到1000000的資料!這就是為什麼第一個分區擁有幾乎所有的資料,它的計算花費了最多的時間;而最後四個分區幾乎沒有資料!

如何解決

  我們可以將資料重新分區。通過對RDD調用.repartition(numPartitions)函數將會使Spark觸發shuffle並且將資料分布到我們指定的分區數中,所以讓我們嘗試將這個加入到我們的代碼中。

  我們除了在.map.flatMap函數之間加上.repartition(8)之外,其他的代碼並不改變。我們的RDD現在同樣擁有8個分區,但是現在的資料將會在這些分區重新分配,修改後的代碼如下:

/** * User: 過往記憶 * Date: 2016年6月24日 * Time: 下午21:16 * bolg: http://www.iteblog.com * 本文地址:http://www.iteblog.com/archives/1695 * 過往記憶部落格,專註於hadoop、hive、spark、shark、flume的技術部落格,大量的乾貨 * 過往記憶部落格公用帳號:iteblog_hadoop */val composite = sc.parallelize(2 to n, 8).map(x => (x, (2 to (n / x)))).repartition(8).flatMap(kv => kv._2.map(_ * kv._1))

新的DAG可視化圖看起來比之前更加複雜,因為repartition操作會有shuffle操作,所有增加了一個stage。

Stage 0和之前一樣,新的 Stage 1看起來和 Stage 0也很類似,每個task大約都處理250000條記錄,並且花費1s的時間。 Stage 2是比較重要的stage,下面是其:

從可以看出,現在的Stage 2比之前舊的Stage 1效能要好很多,這次Stage我們處理的資料和之前舊的Stage 1同樣多,但是這次每個task花費的時候大概為5s,而且每個core得到了高效地使用。

  兩個版本的代碼最後一個Stage大概都運行了6s,所以第一個版本的代碼運行了大約0.5 + 14 + 6 = ~21s;而對資料進行重新分配之後,這次啟動並執行時間大約為0.5 + 1 + 5 + 6 = ~13s。雖然說修改後的代碼需要做一些額外的計算(重新分配資料),但是這個修改卻減少了總的已耗用時間,因為它使得我們可以更加高效地使用我們的資源。

  當然,如果你的目標是尋找質數,有比這裡介紹的更加高效的演算法。但是本文僅僅是用來介紹考慮Spark資料的分布是多麼地重要。增加.repartition函數將會增加Spark總體的工作,但好處可以顯著大於成本

本文翻譯自:Improving Spark Performance With Partitioning

本部落格文章除特別聲明,全部都是原創!
尊重原創,轉載請註明: 轉載自過往記憶(http://www.iteblog.com/)
本文連結: 【通過分區(Partitioning)提高Spark的運行效能】(http://www.iteblog.com/archives/1695)

通過分區(Partitioning)提高Spark的運行效能

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.