先直接上原理圖吧 ! ShuffleMapTask在計算資料之後會為每一個ResultTask建立一份bucket緩衝 , 以及對應的ShuffleBlockFIle磁碟檔案進行儲存 , 在計算完之後會將計算過的相應資訊放入MapStatus , 最後發送給Driver中的DAGScheduler的MapOutputTracker , 每個ResultTask會用BlockStoreShuffleFetcher去MapOutputTracker中的MapStatus擷取需要拉取的資料 , 然後通過底層的BlockManager將資料拉取過來 , 拉取過來的資料就會組成一個內部的RDD , 叫ShuffleRDD , 存入緩衝 , 緩衝不夠存入磁碟 , 最後ResultMap對資料進行彙總產生MapPartitionRDD , 也就是我們所寫程式中action操作後結果RDD
最佳化後的shuffle分析原理圖:
最佳化後的shuffle原理就是根據cpu的數量在ShuffleMap寫入資料到磁碟檔案時只會建立於cpu相應的檔案資料 , 後面在運行新的ShuffleMapTask的時候也只會向同樣的檔案中寫入資料 , 同時會記錄下一些索引來記錄哪個ShuffleMapTask計算的資料在ShuffleBlockFile中的位置 , 多個ShuffleMapTask寫入的資料就叫做一個segment , 也就是說原來的100個ShuffleMapTask對應的100個ResulTask時會建立100*100個磁碟檔案 , 而現在只需要cpu數量乘以ResultMap的數量之積檔案數 , 減少了大量的磁碟檔案讀寫 , 這種最佳化shuffle的方式只需在建立SparkContext的時候設定一個參數即可
在上一章節關於Task的源碼分析最後的關於writer的代碼中:
writer . write ( rdd . iterator ( partition , context ). asInstanceOf [ Iterator [ _ <: Product2 [ Any , Any ]]])
其實這個writer預設的情況下就是HaspShuffleWriter , 調用writer的方法源碼如下:
/** Write a bunch of records to this task's output */ /** * 將每個ShuffleMapTask計算出來的新的RDD的partition資料寫入本地磁碟 */ override def write(records: Iterator[_ <: Product2[K, V]]): Unit = { // 首先判斷,是否需要在map端進行本地彙總 // 比如reduceByKey這樣的運算元操作的話它的dep.aggregator.isDegined就是true , 包括def.mapSideCombine也是true val iter = if (dep.aggregator.isDefined) { if (dep.mapSideCombine) { // 這裡就會執行本地彙總,比如(Hi,1)(Hi,1)那麼此時就會彙總成(Hi,2) dep.aggregator.get.combineValuesByKey(records, context) } else { records } } else { require(!dep.mapSideCombine, "Map-side combine without Aggregator specified!") records } // 如果進行本地彙總那麼就會遍曆資料 , 對每個資料調用partition預設是HashPartition , 產生bucketId // 也就決定了每一份資料要寫入哪個bucket for (elem <- iter) { val bucketId = dep.partitioner.getPartition(elem._1) // 擷取到了bucketId之後就會調用ShuffleBlockManager.formapTask()方法來產生bucketId對應的writer,然後用writer將資料寫入bucket shuffle.writers(bucketId).write(elem) } }
這裡的shuffle是HushShuffleWriter的一個成員變數 , 通過shuffleBlockManager對象的forMapTask方法擷取每個bucketId對應的writer , forMapTask方法源碼如下:
/** * 給每個map task擷取一個ShuffleWriterGroup */ def forMapTask(shuffleId: Int, mapId: Int, numBuckets: Int, serializer: Serializer,