我不想說太多源碼層面的東西,然後把詳細方法一個個列出來,其實沒有多大意義(因爲源碼裏有,再者比我講的清晰明白的大有人在,我沒有必要再重複相同的東西),但是我真的花了好大的精力才把這部分看完,我得記錄下,不然會忘掉
一、spark到底有幾種shuffleManager(shuffle管理類)可以選擇?
首先spark早期的版本(比如1.5.1版本),是有三種shuffle
http://spark.apache.org/docs/1.5.1/configuration.html#shuffle-behavior
但是後來在1.6的版本又把tungsten-sort取消掉了,最後在2的版本又把hash去掉了
所以現在的版本只有一種了!!!雖然配置裏可以寫sort或者tungsten-sort,但是指向的是同一個類
sparkEnv類中:
二、shuffle模式有幾種?
首先是構建shufflehandle(shuffle處理器),獲得write寫出數據(這當中還有一些排序,spill磁盤,磁盤上多個文件合併等,我就不具體說了,很多文章都有說明)
1、構建處理器
SortShuffleManager類中:
有一個註冊shuffle的方法,這個方法是在DAG構建的時候會觸發的(對,就是那個面試的時候很愛問的寬依賴和窄依賴!)
我們可以看到這個方法裏會構建3中ShuffleHandle(handle是處理的意思,所以就是有三種shuffle的處理方式)
然後我們先來看看各自的條件
(1)、BypassMergeSortShuffleHandle
(這個類的名字就是繞過聚合和排序,取名很有哲學!)的生成條件如下:
1、依賴中是否有map端的聚合?(必須沒有)
2、依賴的分區數是否小於spark.shuffle.sort.bypassMergeThreshold這個值(默認是200)
我多說一句,spark.shuffle.sort.bypassMergeThreshold建議不要隨便改動,因爲沒有聚合,spark會直接往磁盤上寫數據,200就要開啓200個io流,如果你手動設置調大了,就會開啓更多的io流,所以值不能太大,會出問題
(2)、SerializedShuffleHandle
(看的出來,序列化的shuffle)的生成條件如下:
1、序列化支持relocation(這是什麼鬼東西,講實話我沒百度到),但是我可以確定的是默認的java序列化是做不了這個事情的,你需要把spark.serializer設置爲org.apache.spark.serializer.KryoSerializer,這樣就ok了!
2、依賴中沒有聚合
3、分區數小於(1 << 24) - 1,也就是16777215
(3)、BaseShuffleHandle
如果上兩條都失敗,那就構建BaseShuffleHandle
2、構建write
根據上面不同的handle創建不同的write(寫類)
SortShuffleManager類中:
好,所以對應關係是
BypassMergeSortShuffleHandle 對應 BypassMergeSortShuffleWriter
SerializedShuffleHandle 對應 UnsafeShuffleWriter 使用 ShuffleExternalSorter 做排序
BaseShuffleHandle 對應 SortShuffleWriter 使用 ExternalSorter 做排序
spark源碼分析之ShuffleExternalSorter(作者:weiqing687 ):https://blog.csdn.net/qq_26222859/article/details/81502251
3、區別和使用場景
(1)、區別
(1)、其實區別在一開始做handle判斷的時候,就可見一斑,bypass適用沒有聚合,分區少(數據量少)的情況,因此他是最先判斷的
(2)、接着是unsafe,unsafe其實是spark的tungsten計劃(可以直接操作服務器的內存,以此來避免GC和JVM裏面因爲對象構建而多佔用的資源),但是有三個前提條件,就是使用kryo和沒有聚合操作外加分區數要小於16777215(不過說句實在話,分區數要大於1600W的任務。。。我還真沒見過,可能我孤陋寡聞了)
(3)、最後這種應該是最通用的了,在以上兩條都不滿足的時候,只能觸發(沒有任何強化特性的)基礎shuffle,他就沒有什麼限制條件了,啥都能做,什麼聚合啊排序啊,或者不想聚合啊都ok,原因在於
SortShuffleWrite類中的write方法:
會根據是否有聚合和排序構建ExternalSorter,然後ExternalSorter類的insertAll方法裏,又會判斷聚合與否,來選擇是使用spark自定義的map保存數據還是buffer緩衝數據(map是可用聚合更新數據的,buffer只是緩衝,然後預估這兩個對象的大小,來spill磁盤)
(2)、使用場景
相信我,這篇文章你能讀到這裏,我已經覺得你很棒了!因爲我自己都覺得很枯燥,好像對於那些想做shuffle層面優化的小夥伴來說,啥都沒得到,接下來我要說說我自己看了這些知識之後的觀點。
1、map端的聚合的疑問?
map端的聚合其實是一個優化,他可以減少數據佔用的空間,10條(spark,1)這樣的數據,我可以用1條(spark,10)來代替,肯定是方便的,但是可以看到如果想使用unsafe的shuffle,那就不能有map端的聚合,那到底有哪些場景適用呢?
我舉幾個例子:
RDD:groupbyKey
是不是覺得,太少了,之前我一時之間也想不到其他的,不過後來
repartition,sort排序,還有一個非常常見的場景join
2、unsafe和基礎的shuffle性能
在完全相同的數據量和處理下,unsafe的性能肯定會比基礎的shuffle性能好,不然spark就不用那麼大費周折的添加這個功能,但是不少場景下,我們是可以通過map的先行聚合來減少數據量,既然減少了數據量,那麼相應內存消耗,io消耗就會減少,因此不能說所有場景下unsafe的性能都比基礎的shuffle好
3、bypass基本只使用數據量少的場景
像本地測試,基本上全是走bypass,大家可以自己在註冊shuffle的方法打斷點跑跑看
4、想使用unsafe優化,是否必須顯示設置kryo
我自己試下,rdd編程的話,必須設置,否則就走java的序列化,那就永遠都用不到unsafe
但如果你用的是DataSet的DSL編程,我發現不設置kryo,他也會走到unsafe裏,不過管他的呢,你直接在配置文件裏寫死kryo它不香嗎?
以上就是我對spark的shuffle模式的總結和見解,歡迎大家留言討論!!
我也看到一些文章還不錯,所以留在下面:
Spark中幾種ShuffleWriter的區別你都知道嗎?(作者:叫我不矜持):https://www.jianshu.com/p/cbab289d51c0
Spark SortShuffleWriter(作者:wangdy12):https://www.jianshu.com/p/541b3648ffd7
spark源碼分析系列(作者:JohnnyBai):https://www.cnblogs.com/johnny666888/p/11259944.html
菜雞一隻,一晃今天是2020年上半年的最後一天了,感覺好快啊~
感謝“甘木”大佬的點撥,我才能寫出這篇文章!