shuffle和排序

shuffle屬於不斷被優化和改進的代碼庫,是MapReduce的“心臟”。

shuffle可以將其定義爲:map的輸出到reduce的輸入(在一些語境中,代表reduce接受map輸出的這部分)

map端


我們知道map產生的輸出是臨時寫到本地磁盤的,但是他並不是簡單的寫到本地磁盤中,這個過程更爲複雜,如圖:

他會首先使用緩衝的方式寫入到內存中,並且處於效率的考慮進行預排序。每個map都有一個緩衝區用於存儲任務輸出,這個緩衝區的大小默認爲100MB,可以通過io.sort.mb屬性調整。一旦緩衝的內容達到預設的閥值(通過io.sort.spill.percent,默認是0.8或80%),一個後臺進程便把內容溢出(spill)到磁盤。在溢出過程map輸出會繼續寫到緩衝區,如果在此期間被填滿,則發生堵塞,直到寫磁盤過程完成。這個溢出寫的過程會將數據寫到mapreduce.cluster.local.dir指定的目錄內。在寫硬盤之前會根據輸出的reduce進行分區(partition),然後對每個分區內容進行排序,如果有combiner函數,則在排序之後執行combiner。每次內存緩衝區達到溢出的閥值,就會新建一個溢出文件(spill file)。最終會有幾個溢出文件,這些溢出文件會被合併成一個已分區且已排序的輸出文件,io.sort.factor控制一次最多能夠合併多少流,默認是10。如果至少有3個溢出文件(這個值由min.num.spills.for.combine屬性設置)則就會在輸出文件寫到磁盤之前在此運行以此combiner。將輸出進行壓縮可以減少輸出及傳遞到reduce的網絡開銷,可以設置mapreduce.compress.map.output設置爲true,使用mapreduce.map.output.compression.codec指定壓縮方法。

reduce端

reduce任務需要集羣中若干個map的輸出作爲其輸入,但是每個map的完成時間並不一樣,所以只要有一個map輸出,reduce就開始複製其輸出,這就是reduce端的複製階段。reduce有少量的複製線程,默認是5個,這個值由mapreduce.reduce.parallel.copies屬性改變。

那麼reduce如何知道從哪臺機器獲取map輸出呢?

map任務完成後,會通知其父tasktracker,tasktracker會通知jobtracker(在MR2中是applicationMaster),從而jobtracker(applicationMaster)知道了tasktracker與map的映射關係,reduce中的一個線程會定期向applicationMaster(或者jobtracker)進行詢問,以便獲取map輸出的位置。

複製完成後,reduce開始進入排序階段(其實是合併節階段,因爲排序是在map端進行的),這個階段合併map輸出,保持其排好的順序。這個合併是循環進行的,可以設置合併因子io.sort.factor,默認是10,即每趟合併10個文件,假設總共50個map,總共進行5趟,最終有5箇中文文件。之後是reduce階段,直接把數據輸入到reduce函數,而不用將這5個文件合併稱一個大文件。reduce函數輸出直接寫到HDFS上。


配置調優

map端的調優屬性:

屬性名稱 類型 默認值 說明
io.sort.mb int 100 map輸出所使用的內存緩衝區大小,以MB爲單位
io.sort.spill.percent float 0.80 緩衝區預設的閥值,超過這個百分比開始將內容溢到磁盤
io.sort.factory int 10 排序文件時一次最多合併的流數,在reduce端也是用
min.num.spills.for.combine int 3 運行combiner所需要最少溢出文件數
mapreduce.compress.map.output Boolean false 壓縮map輸出
mapreduce.map.output.compression.codec Class Name org.apache.hadoop.
io.compress.DefaultCodec
用於map輸出的壓縮編碼器
tasktracker.http.threads int 40 每個tasktracker運行的線程數,用於將map輸出到reduce,在YARN不適用

這個過程總的來說就是要爲shuffle分配更多的內存,但是這時候可能還需要考慮到map函數和reduce函數能夠得到足夠運行的內。所以一般map函數和reduce函數在編寫的時候儘量少佔內存。map端可以通過避免多次溢出寫磁盤來獲得最佳性能,一次是最佳的情況。


reduce端的調優屬性

屬性名稱 類型 默認值 說明
mapreduce.reduce.parallel.copies int 5 用於把map的輸出複製到reduce的線程數
mapreduce.reduce.copy.backoff int 300 在聲明失敗之前,reducer獲取一個map輸出所花的最大時間,以秒
爲單位,如果失敗,reducer可以在此時間內嘗試重傳
io.sort.factor int 10 排序合併時的合併因子
mapreduce.job.shuffle.input.buffer.percent float 0.70 在shuffle階段,分配給map輸出的緩衝區佔堆空間的百分比
mapreduce.iob.shuffle.merge.percent float 0.66 map輸出緩衝區(上面定義的那個)的閥值使用比例,用於啓動合併輸出和磁盤溢出寫的過程
mapreduce.inmem.merge.threshold int 1000 啓動合併輸出和磁盤溢出寫過程的map的輸出的閥值數。0或更小,意味着沒有閥值限制
mapreduce.iob.reduce.input.buffer.percent float 0.0 在reduce過程,在內存中保存map輸出的空間佔整個堆空間的比例。reduce階段開始時
,內存中的map輸出不能大於這個值


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章