通過記憶體建立RDD的分割槽設定
1、示例程式碼
在建立RDD的時候,我們可以從記憶體中進行建立;輸出儲存為檔案。為了演示效果,我們的示例程式碼如下:
1 import org.apache.spark.{SparkConf, SparkContext} 2 3 object Spark02RddParallelizeSet { 4 def main(args: Array[String]): Unit = { 5 System.setProperty("hadoop.home.dir", "C:\\Hadoop\\") 6 val spark = new SparkConf().setMaster("local[*]").setAppName("RddParallelizeSet") 7 val context = new SparkContext(spark) 8 9 val list = List(1, 2, 3, 4) 10 11 // TODO: 從記憶體建立RDD,並且設定並行執行的任務數量 12 // numSlices: Int = defaultParallelism 13 val memoryRDD = context.makeRDD(list, 1) 14 memoryRDD.saveAsTextFile("output") 15 16 val memoryRDD1 = context.makeRDD(list, 2) 17 memoryRDD1.saveAsTextFile("output1") 18 19 val memoryRDD2 = context.makeRDD(list, 3) 20 memoryRDD2.saveAsTextFile("output2") 21 22 val memoryRDD3 = context.makeRDD(list, 4) 23 memoryRDD3.saveAsTextFile("output3") 24 25 val memoryRDD4 = context.makeRDD(list, 5) 26 memoryRDD4.saveAsTextFile("output4") 27 28 // TODO: 結束 29 context.stop() 30 } 31 }
上面的程式碼裡,我們從記憶體中建立了5個RDD,每個RDD設定了不同的分割槽數。
2、執行結果
(1)1個分割槽的RDD,效果如下圖所示:
在output資料夾中,只包含了一個分割槽檔案 part-00000 ,其檔案內容如下圖所示:
(2)2個分割槽的RDD,效果如下圖所示:
在output1資料夾中,包含了兩個分割槽檔案 part-00000 以及 part-00001,二者檔案內容如下圖所示:
(3)3個分割槽的RDD,效果如下圖所示:
在output2資料夾中,包含了三個分割槽檔案 part-00000 、part-00001、part-00002,三者檔案內容如下圖所示:
(4)4個分割槽的RDD,效果如下圖所示:
在output3資料夾中,包含了四個分割槽檔案 part-00000、part-00001、part-00002、part-00003,四者檔案內容如下圖所示:
在output4資料夾中,包含了五個分割槽檔案 part-00000、part-00001、part-00002、part-00003、part-00004,五者檔案內容如下圖所示:
3、分析結果
仔細看看上面圖片中的結果,不難發現其中肯定深藏貓膩。不同分割槽數的設定,都存在不同的輸出效果。要想深究其中緣由,有必要去了解Spark的在這一塊的原始碼實現。進入到 makeRDD( ) 這個方法中,可以看到如下原始碼:
在圖片中用紅色方框框起來的部分,是一個方法,其中傳入了兩個引數,第一個 seq 就是在我們自己寫的程式碼中,自己定義的那個 List;第二個 numSlices 就是我們自己寫的程式碼中的那個分割槽數。
再點進 parallelize( seq, numSlices) 這個方法中去,可以看到如下原始碼:
紅色線框框出的這個名為 getPartitions 的方法,返回的是一個分割槽的陣列,ParallelCollectionRDD.slice( ) 呼叫的是 ParallelCollectionRDD 伴生物件裡面的一個方法,看樣子應該是這個方法裡的程式碼規定了怎麼進行分割槽的劃分了,於是,進到這個方法裡面,果不其然,可以看到如下程式碼塊:
在 slice[T: ClassTag](seq: Seq[T], numSlices: Int): Seq[Seq[T]] 這個方法中,我們可以看到一個關於 seq 這個傳入的引數的模式匹配:
在上圖的這一段程式碼中,模式匹配第一個匹配的是 Range 型別,很明顯與我們傳入的 List 型別不一致,因此 case r: Range 這一塊的匹配程式碼可以跳過。第二個匹配的也是Range,相比於第一個匹配的Range 整形型別 而言,第二個則可以匹配更多種型別的 Range。當然第二個case也不是我們要看的。那麼就剩下第三個 "case _" 這種情況了。
在第三個情況中,首先將 我們傳入的 List 轉換為了一個 array,為什麼要有這一步,原始碼中也給出了註釋:To prevent O(n ^ 2) operations for List etc。之後呼叫了positions( ) 方法,將轉換後的陣列的長度以及分割槽數量傳入,該方法原始碼如下:
我的分析過程如下,資料是(1,2,3,4),分割槽數量是1的情況:
4、小結