mapreduce shuffle過程問答

      通過hadoop權威指南學習hadoop,對shuffle過程一直很疑惑,經過查看網上多個帖子,最終 完成此篇問答總結。

    1.什麼叫shuffle

    map任務輸出到reducer任務輸入之間的過程就叫做shuffle

     

    2.每個map任務都有對應的緩存嗎?默認是多少,怎麼配置這個值的大小?

    每個map任務都有一個緩存支持輸出,默認大小是100m,可以通過屬性io.sort.mb配置

     

    3.什麼時候觸發緩存的數據寫入磁盤

    當緩存的容量達到緩存一定比例時觸發,這個比例由屬性Io.sort.spill.percent配置,默認是0.8

     

    4.爲什麼需要設置寫入比例

    達到一定比例後,由於寫緩存和讀緩存是可以同時並行執行的,這會降低把緩存數據騰空的時間,從而提高效率

     

    5.怎麼理解緩存叫做環形緩存

    緩存有一個閥值比例配置,當達到整個緩存的這個比例時,會觸發spill操作;觸發時,map輸出還會接着往剩下的空間寫入,但是寫滿的空間會被鎖定,數據溢出寫入磁盤。

    當這部分溢出的數據寫完後,空出的內存空間可以接着被使用,形成像環一樣的被循環使用的效果。如圖:

    圖一表示剛好達到溢出比例的結構:


     

    圖二表示有數據開始spill到磁盤,並且新的數據繼續往空的空間寫入


     

    圖三表示溢出的數據都被寫入磁盤後緩存的狀態


     

    圖四表示溢出前剩餘的空間被寫滿後繼續從頭(以前被溢出的數據所佔空間)開始寫入


     

    以上四個圖展示的過程爲,尾部寫滿後從頭部接着寫,形成類似環狀的形態

     

    6.緩存的結構是什麼樣的?

    如圖:


    數據從右到左開始寫入,關於此keyvalue的元數據(partition,keystart,valuestart)寫入左邊的索引區

     

     

     

    7.怎麼理解partition的過程

    分做兩步:

    1.標記key value所屬與的分區

        map輸出的時候,寫入緩存之前,會調用partition函數,計算出數據所屬的分區,並且把這個元 數據存儲起來

    2.把屬與同一分區的數據合併在一起

        當數據達到溢出的條件時(即達到溢出比例,啓動線程準備寫入文件前),讀取緩存中的數據和分區元數據,然後把屬與同一分區的數據合併到一起

     

    8.map任務端數據輸出排序過程是什麼樣的?

    當達到溢出條件後,比如默認的是0.8,則會讀出80M的數據,根據之前的分區元數據,按照分區號進行排序,這樣就可實現同一分區的數據都在一起,然後再根據map輸出的key進行排序。最後實現溢出的文件內是分區的,且分區內是有序的

     

    9.map任務數據輸出後所做的combinemerge有什麼區別?

    1combine主要是把形如aa:1,aa:2這樣的key值相同的數據進行計算,計算規則與reduce一致,比如:當前計算是求key對應的值求和,則combine操作後得到aa:3這樣的結果。

           map輸出數據根據分區排序完成後,在寫入文件之前會執行一次combine操作(前提是設客戶端設置了這個操作);如果map輸出比較大,溢出文件個數大於3(此值可以通過屬性min.num.spills.for.combine配置)時,在merge的過程(多個spill文件合併爲一個大文件)中還會執行combine操作

    注意事項:不是每種作業都可以做combine操作的,只有滿足以下條件纔可以:

     a)reduce的輸入輸出類型都一樣,因爲combine本質上就是用的reduce

     b)計算邏輯上,combine操作後不會影響計算結果,像求和就不會影響

     

    2)merge操作是對形如a:1 a:2這樣的數據最後形成{"a":[1,2]}這樣的數據,作爲reduce任務的輸入

        map輸出的時候,只有在多個溢出文件合併爲一個大文件時纔會執行merge操作

     

    無論是combine還是merge都是爲了增加數據的密度,減少數據的傳輸和存儲,提高系統的效率

     

    10.怎麼標記溢出文件中不同分區的數據

    每次溢出的數據寫入文件時,都按照分區的數值從小到大排序,內部存儲是以tag的方式區分不同分區的數據;同時生成一個索引文件,這個索引文件記錄分區的描述信息,包括:起始位置、長度、以及壓縮長度,這些信息存儲在IndexRecord結構裏面。一個spill文件中的多個段的索引數據被組織成SpillRecord結構,SpillRecord又被加入進indexCacheList中。

     

    11.怎樣把所有的spill文件合併進入唯一一個文件

          map輸出數據比較多的時候,會生成多個溢出文件,任務完成的最後一件事情就是把這些文件合併爲一個大文件。合併的過程中一定會做merge操作,可能會做combine操作。

    1)如果生成的文件太多,可能會執行多次合併,每次最多能合併的文件數默認爲10,可以通過屬性min.num.spills.for.combine配置

    2)多個溢出文件合併是,同一個分區內部也必須再做一次排序,排序算法是多路歸併排序

    3)是否還需要做combine操作,一是看是否設置了combine,二是看溢出的文件數是否大於等於3,請看第9點的介紹

    4)最終生成的文件格式與單個溢出文件一致,也是按分區順序存儲,並且有一個對應的索引文件,記錄每個分區數據的起始位置,長度以及壓縮長度。這個索引文件名叫做file.out.index

     

    12.reducer怎麼知道去哪兒讀取map輸出呢?

    當任務執行完成後,tasktracker會通知jobtracker;當reducer所在的reducer通過心跳請求任務時,jobtracker會告訴reducer去哪兒拷貝數據

     

    13.reducer怎麼知道自己應該讀取那個分區呢?

    這個問題,我一直沒有搞明白,目前猜測是按照順序,比如第一個分配的reudcer任務對應的分區號是0;還有一種可能是,執行map任務的tasktracker把分區索引告訴了jobtracker,然後jobtracker明確告訴reducer去哪兒讀取輸出,讀取的是那個分區的數據。

     

    14.reduce端的過程是什麼樣的?


     

    reduce的運行是分成三個階段的。分別爲copy->sort->reduce。由於job的每一個map都會根據reduce(n)數將數據分成map 輸出結果分成n個partition,

    所以map的中間結果中是有可能包含每一個reduce需要處理的部分數據的。所以,爲了優化reduce的執行時間,hadoop中是等job的第一個map結束後,

    所有的reduce就開始嘗試從完成的map中下載該reduce對應的partition部分數據。這個過程就是通常所說的shuffle,也就是copy過程。

     

    Reduce task在做shuffle時,實際上就是從不同的已經完成的map上去下載屬於自己這個reduce的部分數據,由於map通常有許多個,

    所以對一個reduce來說,下載也可以是並行的從多個map下載,這個並行度是可以調整的,調整參數爲:mapred.reduce.parallel.copies(default 5)。

    默認情況下,每個只會有5個並行的下載線程在從map下數據,如果一個時間段內job完成的map有100個或者更多,那麼reduce也最多隻能同時下載5個map的數據,

    所以這個參數比較適合map很多並且完成的比較快的job的情況下調大,有利於reduce更快的獲取屬於自己部分的數據。

     

    reduce的每一個下載線程在下載某個map數據的時候,有可能因爲那個map中間結果所在機器發生錯誤,或者中間結果的文件丟失,或者網絡瞬斷等等情況,

    這樣reduce的下載就有可能失敗,所以reduce的下載線程並不會無休止的等待下去,當一定時間後下載仍然失敗,那麼下載線程就會放棄這次下載,

    並在隨後嘗試從另外的地方下載(因爲這段時間map可能重跑)。所以reduce下載線程的這個最大的下載時間段是可以調整的,

    調整參數爲:mapred.reduce.copy.backoff(default 300秒)。如果集羣環境的網絡本身是瓶頸,那麼用戶可以通過調大這個參數來避免reduce下載線程被誤判爲失敗的情況。不過在網絡環境比較好的情況下,沒有必要調整。通常來說專業的集羣網絡不應該有太大問題,所以這個參數需要調整的情況不多。

     

    Reduce將map結果下載到本地時,同樣也是需要進行merge的,所以io.sort.factor的配置選項同樣會影響reduce進行merge時的行爲,該參數的詳細介紹上文已經提到,

    當發現reduce在shuffle階段iowait非常的高的時候,就有可能通過調大這個參數來加大一次merge時的併發吞吐,優化reduce效率。

     

    Reduce在shuffle階段對下載來的map數據,並不是立刻就寫入磁盤的,而是會先緩存在內存中,然後當使用內存達到一定量的時候才刷入磁盤。

    這個內存大小的控制就不像map一樣可以通過io.sort.mb來設定了,而是通過另外一個參數來設置:mapred.job.shuffle.input.buffer.percent(default 0.7),

    這個參數其實是一個百分比,意思是說,shuffile在reduce內存中的數據最多使用內存量爲:0.7 × maxHeap of reduce task。也就是說,

    如果該reduce task的最大heap使用量(通常通過mapred.child.java.opts來設置,比如設置爲-Xmx1024m)的一定比例用來緩存數據。默認情況下,

    reduce會使用其heapsize的70%來在內存中緩存數據。如果reduce的heap由於業務原因調整的比較大,相應的緩存大小也會變大,這也是爲什麼reduce

    用來做緩存的參數是一個百分比,而不是一個固定的值了。

     

    假設mapred.job.shuffle.input.buffer.percent爲0.7,reduce task的max heapsize爲1G,那麼用來做下載數據緩存的內存就爲大概700MB左右,

    這700M的內存,跟map端一樣,也不是要等到全部寫滿纔會往磁盤刷的,而是當這700M中被使用到了一定的限度(通常是一個百分比),就會開始往磁盤刷。

    這個限度閾值也是可以通過job參數來設定的,設定參數爲:mapred.job.shuffle.merge.percent(default 0.66)。如果下載速度很快,

    很容易就把內存緩存撐大,那麼調整一下這個參數有可能會對reduce的性能有所幫助。

     

    當reduce將所有的map上對應自己partition的數據下載完成後,就會開始真正的reduce計算階段(中間有個sort階段通常時間非常短,幾秒鐘就完成了,

    因爲整個下載階段就已經是邊下載邊sort,然後邊merge的)。當reduce task真正進入reduce函數的計算階段的時候,有一個參數也是可以調整reduce的計算行爲。

    也就是:mapred.job.reduce.input.buffer.percent(default 0.0)。由於reduce計算時肯定也是需要消耗內存的,而在讀取reduce需要的數據時,

    同樣是需要內存作爲buffer,這個參數是控制,需要多少的內存百分比來作爲reduce讀已經sort好的數據的buffer百分比。默認情況下爲0,也就是說,

    默認情況下,reduce是全部從磁盤開始讀處理數據。如果這個參數大於0,那麼就會有一定量的數據被緩存在內存並輸送給reduce,當reduce計算邏輯消耗內存很小時,

    可以分一部分內存用來緩存數據,反正reduce的內存閒着也是閒着。

     

    參考資料

    1.http://www.linuxidc.com/Linux/2011-11/47053.htm

    2.http://blog.csdn.net/mrtitan/article/details/8711366

    3.http://www.alidata.org/archives/1470

    4.http://blog.sina.com.cn/s/blog_4a1f59bf0100ssap.html

    5.http://blog.csdn.net/HEYUTAO007/article/details/5725379

     

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