5) 當所有的Map 輸出都被拷貝後,Reduce 任務進入排序階段(更恰當的說應該是歸併階段,因爲排序在Map 端就已經完成),這個階段會對所有的Map 輸出進行歸併排序,這個工作會重複多次才能完成。
MapReduce作業shuffle分析
1、流程圖
5) 當所有的Map 輸出都被拷貝後,Reduce 任務進入排序階段(更恰當的說應該是歸併階段,因爲排序在Map 端就已經完成),這個階段會對所有的Map 輸出進行歸併排序,這個工作會重複多次才能完成。
2、流程概要
2.1 map輸出內存
2.2 內存到硬盤之前,hash分區、每個分區中內排序、有combiner則運行
2.3 硬盤數據合併merge
2.4 reduce數據fetch map的輸出文件
2.5 複製來的輸出文件合併merge
2.6 最後reduce
3、流程解析
3.1 map端
1) 當Map開始產生輸出的時候,他並不是簡單的把數據寫到磁盤,因爲頻繁的操作會導致性能嚴重下降,他的處理更加複雜,數據首先是寫到內存中的一個緩衝區,並作一些預排序,以提升效率
2) 每個Map任務都有一個用來寫入輸出數據的循環內存緩衝區,這個緩衝區默認大小是100M,可以通過io.sort.mb屬性來設置具體的大小,當緩衝區中的數據量達到一個特定的閥值(io.sort.mb * io.sort.spill.percent,其中io.sort.spill.percent 默認是0.80)時,系統將會啓動一個後臺線程把緩衝區中的內容spill 到磁盤。在spill過程中,Map的輸出將會繼續寫入到緩衝區,但如果緩衝區已經滿了,Map就會被阻塞直道spill完成。spill線程在把緩衝區的數據寫到磁盤前,會對他進行一個二次排序,首先根據數據所屬的partition排序,然後每個partition中再按Key排序。輸出包括一個索引文件和數據文件,如果設定了Combiner,將在排序輸出的基礎上進行。Combiner就是一個Mini
Reducer,它在執行Map任務的節點本身運行,先對Map的輸出作一次簡單的Reduce,使得Map的輸出更緊湊,更少的數據會被寫入磁盤和傳送到Reducer。Spill文件保存在由mapred.local.dir指定的目錄中,Map任務結束後刪除。
3) 每當內存中的數據達到spill閥值的時候,都會產生一個新的spill文件,所以在Map任務寫完他的最後一個輸出記錄的時候,可能會有多個spill文件,在Map任務完成前,所有的spill文件將會被歸併排序爲一個索引文件和數據文件。如圖3 所示。這是一個多路歸併過程,最大歸併路數由io.sort.factor 控制(默認是10)。如果設定了Combiner,並且spill文件的數量至少是3(由min.num.spills.for.combine 屬性控制),那麼Combiner 將在輸出文件被寫入磁盤前運行以壓縮數據。
4) 對寫入到磁盤的數據進行壓縮(這種壓縮同Combiner 的壓縮不一樣)通常是一個很好的方法,因爲這樣做使得數據寫入磁盤的速度更快,節省磁盤空間,並減少需要傳送到Reducer 的數據量。默認輸出是不被壓縮的, 但可以很簡單的設置mapred.compress.map.output爲true 啓用該功能。壓縮所使用的庫由mapred.map.output.compression.codec來設定
5) 當spill 文件歸併完畢後,Map 將刪除所有的臨時spill 文件,並告知TaskTracker 任務已完成。Reducers 通過HTTP 來獲取對應的數據。用來傳輸partitions 數據的工作線程個數由tasktracker.http.threads 控制,這個設定是針對每一個TaskTracker 的,並不是單個Map,默認值爲40,在運行大作業的大集羣上可以增大以提升數據傳輸速率。
3.2 reduce端
1) Map的輸出文件放置在運行Map任務的TaskTracker的本地磁盤上(注意:Map輸出總是寫到本地磁盤,但是Reduce輸出不是,一般是寫到HDFS),它是運行Reduce任務的TaskTracker所需要的輸入數據。Reduce任務的輸入數據分佈在集羣內的多個Map任務的輸出中,Map任務可能會在不同的時間內完成,只要有其中一個Map任務完成,Reduce任務就開始拷貝他的輸出。這個階段稱爲拷貝階段,Reduce任務擁有多個拷貝線程,可以並行的獲取Map輸出。可以通過設定mapred.reduce.parallel.copies來改變線程數。
2) Reduce是怎麼知道從哪些TaskTrackers中獲取Map的輸出呢?當Map任務完成之後,會通知他們的父TaskTracker,告知狀態更新,然後TaskTracker再轉告JobTracker,這些通知信息是通過心跳通信機制傳輸的,因此針對以一個特定的作業,jobtracker知道Map輸出與tasktrackers的映射關係。Reducer中有一個線程會間歇的向JobTracker詢問Map輸出的地址,直到把所有的數據都取到。在Reducer取走了Map輸出之後,TaskTracker不會立即刪除這些數據,因爲Reducer可能會失敗,他們會在整個作業完成之後,JobTracker告知他們要刪除的時候纔去刪除。
3) 如果Map輸出足夠小,他們會被拷貝到Reduce TaskTracker的內存中(緩衝區的大小由mapred.job.shuffle.input.buffer.percnet控制),或者達到了Map輸出的閥值的大小(由mapred.inmem.merge.threshold控制),緩衝區中的數據將會被歸併然後spill到磁盤。
4) 拷貝來的數據疊加在磁盤上,有一個後臺線程會將它們歸併爲更大的排序文件,這樣做節省了後期歸併的時間。對於經過壓縮的Map 輸出,系統會自動把它們解壓到內存方便對其執行歸併。5) 當所有的Map 輸出都被拷貝後,Reduce 任務進入排序階段(更恰當的說應該是歸併階段,因爲排序在Map 端就已經完成),這個階段會對所有的Map 輸出進行歸併排序,這個工作會重複多次才能完成。
6) 假設這裏有50 個Map 輸出(可能有保存在內存中的),並且歸併因子是10(由io.sort.factor控制,就像Map 端的merge 一樣),那最終需要5 次歸併。每次歸併會把10個文件歸併爲一個,最終生成5 箇中間文件。在這一步之後,系統不再把5 箇中間文件歸併成一個,而是排序後直接“喂”給Reduce 函數,省去向磁盤寫數據這一步。最終歸併的數據可以是混合數據,既有內存上的也有磁盤上的。由於歸併的目的是歸併最少的文件數目,使得在最後一次歸併時總文件個數達到歸併因子的數目,所以每次操作所涉及的文件個數在實際中會更微妙些。譬如,如果有40
個文件,並不是每次都歸併10 個最終得到4 個文件,相反第一次只歸併4 個文件,然後再實現三次歸併,每次10 個,最終得到4 個歸併好的文件和6 個未歸併的文件。要注意,這種做法並沒有改變歸併的次數,只是最小化寫入磁盤的數據優化措施,因爲最後一次歸併的數據總是直接送到Reduce 函數那裏。在Reduce 階段,Reduce 函數會作用在排序輸出的每一個key 上。這個階段的輸出被直接寫到輸出文件系統,一般是HDFS。在HDFS 中,因爲TaskTracker 節點也運行着一個DataNode 進程,所以第一個塊備份會直接寫到本地磁盤。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.