MapReduce的原理

 

        Hadoop中的MapReduce是一個使用簡易的軟件框架,基於它寫出來的應用程序能夠運行在由上千個商用機器組成的大型集羣上,並以一種可靠容錯的方式並行處理上T級別的數據集。

     一個MapReduce作業(job)通常會把輸入的數據集切分爲若干獨立的數據塊,由map任務(task)以完全並行的方式處理它們。框架會對map的輸出先進行排序,然後把結果輸入給reduce任務。通常作業的輸入和輸出都會被存儲在文件系統中。整個框架負責任務的調度和監控,以及重新執行已經失敗的任務。

      通常,MapReduce框架和分佈式文件系統是運行在一組相同的節點上的,也就是說,計算節點和存儲節點通常在一起。這種配置允許框架在那些已經存好數據的節點上高效地調度任務,這可以使整個集羣的網絡帶寬被非常高效地利用。

      MapReduce框架由一個單獨的master JobTracker和每個集羣節點一個slave TaskTracker共同組成。master負責調度構成一個作業的所有任務,這些任務分佈在不同的slave上,master監控它們的執行,重新執行已經失敗的任務。而slave僅負責執行由master指派的任務。

       應用程序至少應該指明輸入輸出的位置(路徑),並通過實現合適的接口或抽象類提供map和reduce函數。再加上其他作業的參數,就構成了作業配置(job configuration)。然後,Hadoop的jobclient提交作業(jar包可執行程序等)和配置信息給JobTracker,後者負責分發這些軟件和配置信息給slave、調度任務並監控它們的執行,同時提供狀態和診斷信息給job-client。

    輸入與輸出:MapReduce框架運轉在<key,value>鍵值對上,也就是說,框架把作業的輸入看爲是一組<key,value>鍵值對,同樣也產出一組<key,value>鍵值對做爲作業的輸出,這兩組鍵值對的類型可能不同。

      框架需要對key和value的類(classes)進行序列化操作,因此,這些類需要實現Writable接口。另外,爲了方便框架執行排序操作,key類必須實現WritableComparable接口。一個MapReduce作業的輸入和輸出類型如下所示:

(input)<k1,v1>->map-><k2,v2>->combine-><k2,v2>->reduce-><k3,v3>(output)

Hadoop中的MapReduce工作流程簡介

一般來講,Hadoop的一個簡單的MapReduce任務執行流程如圖5.2,5.3所示

標題

 

 

標題

1).JobTracker負責分佈式環境中實現客戶端創建任務並提交。

2).InputFormat模塊負責做Map前的預處理,主要包括以下幾個工作:驗證輸入的格式是否符合JobConfig的輸入定義,可以是專門定義或者是Writable的子類。將input的文件切分爲邏輯上的輸入InputSplit,因爲在分佈式文件系統中blocksize是有大小限制的,因此大文件會被劃分爲多個較小的block。通過RecordReader來處理經過文件切分爲Inputsplit的一組records,輸出給Map。因爲Inputsplit是邏輯切分的第一步,如何根據文件中的信息來具體切分還需要RecordReader完成。

3).將RecordReader處理後的結果作爲Map的輸入,然後Map執行定義的Map邏輯,輸出處理後的(key,value)對到臨時中間文件。

4).Combiner是可選擇的,它的主要作用是在每一個Map執行完分析以後,在本地優先作Reduce的工作,減少在Reduce過程中的數據傳輸量。

5).Partitioner也是選擇配置,主要作用是在多個Reduce的情況下,指定Map的結果由某一個Reduce處理,每一個Reduce都會有單獨的輸出文件。

6).Reduce執行具體的業務邏輯,即用戶編寫的處理數據得到結果的業務,並且將處理結果輸出給OutputFormat。

7).OutputFormat的作用是,驗證輸出目錄是否已經存在和輸出結果類型是否複合Config中配置類型,如果都成立,則輸出Reduce彙總後的結果。

Hadoop中MapReduce的任務調度

首先要保證master節點的NameNode,SecondedNameNode,JobTracker和slaves節點的DataNode,TaskTracker都已經啓動。通常MapRedcue作業是通過JobClient.rubJob(job)方法向master節點的JobTracker提交的,JobTracker接到JobClient的請求後把其加入作業隊列中。JobTracker一直在等待JobClient通過RPC向其提交作業,而TaskTracker一直通過RPC向JobTracker發送心跳信號詢問有沒有任務可做,如果有,則請求JobTracker派發任務給它執行。如果JobTracker的作業隊列不爲空,則TaskTracker發送的心跳將會獲得JobTracker給它派發的任務。這是一個主動請求的任務:slave的TaskTracker主動向master的JobTracker請求任務。當TaskTracker接到任務後,通過自身調度在本slave建立起Task,執行任務。圖5.4是MapReduce任務請求調度的過程示意圖:

標題

 

具體來說,這個過程包括兩個步驟:

1).JobClient提交作業

JobClient.runJob(job)靜態方法會實例化一個JobClient的實例,然後用此實例的submitJob(job)方法向JobTracker提交作業。此方法會返回一個RunningJob對象,它用來跟蹤作業的狀態。作業提交完畢後,JobClient會根據此對象開始關注作業的進度,直到作業完成。submitJob(job)內部是通過調用submitJobInternal(job)方法完成實質性的作業提交的。submitJobInternal(job)方法首先會向hadoop分佈系統文件系統(HDFS)依次上傳三個文件:job.jar,job.split和job.xml。job.jar裏面包含了執行此任務需要的各種類,比如Mapper,Reducer等實現;job.split是文件分塊的相關信息,比如有數據分多少個塊,塊的大小(默認64M)等。job.xml是有關的作業配置,例如Mapper,Combiner,Reducer的類型,輸入輸出格式的類型等。

2).JobTacker調度作業

JobTracker接到JobClient提交的作業後,即在JobTracker.submitJob(job)方法中,首先產生一個JobInProgress對象。此對象代表一道作業,它的作用是維護這道作業的所有信息,包括作業相關信息JobProfile和最近作業狀態JobStatus,並將作業所有規劃的Task登記到任務列表中。隨後JobTracker將此JobInProgress對象通過listener.jobAdded(job)方法加入到調度隊列中,並用一個成員變量jobs來維護所有的作業。然後等到有TaskTracker空閒,使用JobTracker.AssignTask(tasktracker)來請求任務,如果調度隊列不空,程序便通過調度算法取出一個task交給來請求的TaskTracker去執行。至此,整個任務分配過程基本完成,各個類的相互關係和依賴性如圖5.5所示:

 

標題

 

       現在,Hadoop自帶的調度策略規定是先進先出(FIFO)的,很多系統也是直接用它。雖然FIFO策略簡單穩定,但隨着用戶和服務的日益增多,特別是服務等級的區分日益明顯,高資費的用戶希望擁有更優先的服務,因此FIFO沒有辦法適應越來越多的Hadoop商業應用需求。相關的開發種也有人考慮隊列容量分配和公平隊列算法,但算法實現都不夠實用,也沒有認真分析Hadoop中服務優先區分的具體要求。因此,本文將重新考慮其調度策略,在考慮服務等級區分和儘量保證公平的想法基礎上,提出新的調度算法,並編碼實現。

基於優先級的加權輪轉算法

調度算法相關研究

       由於Hadoop的MapReduce調度是由tasktracker主動向jobtracker請求的,其原理類似於普通的非搶佔式操作系統調度,即任務一旦分配,就不可中斷。根據調研,已有典型調度算法如下:

1).先進先出算法(FIFO:Fisrt In First Out):該算法按照進程進入就緒隊列的先後順序來選擇。即每當進入進程調度,總是把就緒隊列的隊首進程投入運行。Hadoop自帶的調度算法就是FIFO。

2).時間片輪轉算法(RR:Round-Robin):主要是分時系統中使用的一種調度算法。輪轉發的基本思想是:將cpu劃分成一個個時間片,就緒隊列中的就緒進程輪流運行一個時間片。在Hadoop,可以將每個task請求看作一個時間片,用輪轉的方法來調度可以使得所有job的task輪流被調度。

3).最高優先級算法(HPF:Height Priority First):該算法調度進程時,每次將處理即分配給具有最高優先級的就緒進程。進行優先數的設置可以時靜態的,也可以是動態的。靜態優先數是在進程創建是根據進程初始特性或用戶要求而確定的,在進程運行期間不能再改變。動態優先數則是指再進程創建時先確定一個初始優先數,以後可以在進程運行中隨着進程的特性改變。

4).加權輪循算法(WRR:Weighted Round Robin):在網絡路由領域,不少研究考慮了加權的輪循算法,本文將該算法思想引入Hadoop的任務調度中,結合Hadoop任務處理的特性,提出了適合於該平臺的特有的基於優先級的加權輪轉算法。

基於優先級的加權輪轉算法

考慮到TaskTracker主動請求task的模式和hadoop任務調度體系非搶佔式的特點;爲了使調度的任務避免長期的等待,同時各個任務調度優先級又能夠根據實際情況調整,我們將基於加權輪轉調度算法的基本思想,綜合考慮hadoop網絡調度需求的實際情況,提出適合Hadoop任務調度的基於優先級的加權輪轉調度算法(Priority Based Weighted Round Robin,PBWRR)。

算法基本思想爲:將各個待運行的job放入一個隊列,在不加權的情況下,輪流將任務的task交給tasktracker去執行。加權的情況下,權重較大的job可以在一次輪流中執行多個tasks。

算法的基本步驟是:

1).在tasktracker資源可用的情況下,tasktracker主動向jobtracker提交任務分配請求。

2).Jobtraker接受到tasktraker的任務分配請求後,考慮調度隊列中當前job的一個task交給請求的tasktracker去執行,更新該job的剩餘task信息,同時將該job的thisRoundTask值減1,如果減1後thisRoundTask結果小於1,將指針移動到下一個job;否則指針不動,等待下一個tasktracker請求的到來。

3).當指針到達隊尾時,更新整個隊列的相關信息,如果有job完成工作或者新的job加入,則重新計算每個job的thisRoundTask值,並將指針移動到隊列的開始。

整個算法的流程示意如圖5.6

 

標題

 

jobQueue的調度隊列中每個元素jobInfo的數據結構爲:

Class jobInfo

{

Int JobId;

Int jobSize;

Int taskNum;

Int meanTaskSize;

Int priority;

Int weight;

Int ThisRountTask;

}

整個調度中涉及兩種隊列,jobQueue和taskQueue[].其中jobQueue的元素爲jobInfo,每個taskQueue中的所有元素對應一個job的所有map或reduce的task。在本文編程實現中,所有queue均使用數組實現。

分配任務的方法assignTasks具體算法下:

synchronized List<Task>assignTasks(TaskTrackerStatustracker)

{

1).首先考察整個jobQueue中是否有等待處理的map task或reduce task任務,如果沒有返回null。

2).考察當前jobQueue中指針指向的jobInfo,根據該jobInfo的JobId找到對應的taskQueue[i]。

3).考察其爲maptask Queue還是Reducetask Queue,如果是Reducetask Queue,需要考慮其對應的maptask是否已完成,如果其對應的maptask沒有完成,需要將jobQueue的指針後移,重新考慮步驟2。如果是maptask Queue或者是Reducetask Queue且其對應的maptask已經完成,則分配該Queue的隊首task給請求任務的tasktracker。

4).從分配task的taskQueue中刪除該task並更新該taskQueue的統計信息,更新jobQueue中對應的jobInfo信息(包括thisRoundTask-1,doneTask+1等)。如果更新後的jobInfo的thisRoundTask值小於1,則將jobQueue指針後移,否則指針保持不動。

5).如果指針已經移動到隊尾,則根據jobQueue更新後的jobInfo信息,如果有job完成任務或者有新的job加入,則重新計算所有的jobweight,即每個job的thisRoundTask值,再將指針移動至隊首。

6).返回步驟3所分配的task。

}

其中步驟5中的權重及其thisRoundTask計算方法如下:

updateJobQueue(jobQueue queue1)

{

weight=calculateWeight(priority);

thisRoundTask=Min(floor(weight),taskNum);

}

其中priority的取值在運行時配置,由於雲計算提供的業務種類繁多,收費和服務質量要求也各不相同,因此priority的取值也比較豐富。但在新增業務及其priority時,必須參考以前所有業務的priority取值,從而保持整個系統的相對公平性。當然,這應該時在業務規劃時考慮的問題,而不是本文思考的重點。在本文的實驗中,我們就簡單的將priority取值爲1、2、3等整數值。thisRoundTask爲weight下取整和該job剩餘的taskNum中取較小的值。

權重計算方法推導過程:

每個job[i]的meanTaskSize[i]=jobSize[i]taskNum[i];如果job[i]的權重爲weight[i],則整個系統一個調度週期處理的數據量爲:

同時,如果每個job[i]有優先級priority[i],爲了保證其優先級的有效性,我們應該使得在這個週期中該job的數據處理量佔整個系統數據處理量的百分比基本等於該job優先級值佔整個系統優先級值之和的百分比,即:

 

其中priority[i]和meanTaskSize[i]都是已知的,我們唯一需要考慮的就是整個系統一個週期處理的數據量S。

考慮系統的處理能力,由於系統應用的絕大多數task的size都應該爲blockSize,因此我們考慮整個系統所支持的MapTask能力(因爲一般情況下ReduceTask需求小於mapTask)taskAbility,因此S可以定義爲公式5.4

其中num表示一個週期內平均每個job執行的task數量,jobNum爲待處理

的job個數,在本文實驗中,我們對num取值2。

假設整個系統有m個pc組成,第i個pc可以支持t[i]個tasktracker,每個tasktracker的單位處理能力爲pt[i],整個系統的單位處理能力就爲:

其中p[i]爲這臺pc分配給Hadoop的整個處理能力,因爲在單核計算機中,CPU是進程時分複用的,因此每個tasktracker的處理能力和每個單位tasktracker的處理能力的乘積就等於pc分配給Hadoop的整個處理能力p[i]。我們通過P值和S值可以估計系統一個週期運行的大致時間,但由於調度和實際任務的size問題,實際運行時間和估計時間由一定差距。

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