1. 概述
1970年,IBM的研究員E.F.Codd博士在刊物《Communication of the ACM》上發表了一篇名為“A Relational Model of Data for Large Shared Data Banks”的論文,提出了關聯式模式的概念,標誌著關聯式資料庫的誕生,隨後幾十年,關聯式資料庫及其結構化查詢語言 (SQL)SQL成為程式員必須掌握的基本技能之一。
2005年4月,Jeffrey Dean和Sanjay Ghemawat在國際會議OSDI上發表“MapReduce: Simplified Data Processing on Large Cluster”,標誌著google的大規模資料處理系統MapReduce公開。受這篇論文的啟發,當年秋天,Hadoop 由 Apache Software Foundation 公司作為 Lucene 的子項目 Nutch 的一部分正式被引入,2006 年 3 月份,MapReduce 和 Nutch Distributed File System (NDFS) 分別被納入稱為 Hadoop 的項目中。如今,Hadoop已經被超過50%的互連網公司使用,其他很多公司正準備使用Hadoop來處理海量資料,隨著Hadoop越來越受歡迎,也許在將來的某段時間,Hadoop會成為程式員必須掌握的技能之一,如果真是這樣的話,學會如何在Hadoop上編寫MapReduce程式便是學習Hadoop的開始。
本文介紹了在Hadoop上編寫MapReduce程式的基本方法,包括MapReduce程式的構成,不同語言開發MapReduce的方法等。
2. Hadoop 作業構成
2.1 Hadoop作業執行流程
使用者配置並將一個Hadoop作業提到Hadoop架構中,Hadoop架構會把這個作業分解成一系列map tasks 和reduce tasks。Hadoop架構負責task分發和執行,結果收集和作業進度監控。
下圖給出了一個作業從開始執行到結束所經曆的階段和每個階段被誰控制(使用者 or Hadoop架構)。
下圖詳細給出了使用者編寫MapRedue作業時需要進行那些工作以及Hadoop架構自動完成的工作:
在編寫MapReduce程式時,使用者分別通過InputFormat和OutputFormat指定輸入和輸出格式,並定義Mapper和Reducer指定map階段和reduce階段的要做的工作。在Mapper或者Reducer中,使用者只需指定一對key/value的處理邏輯,Hadoop架構會自動順序迭代解析所有key/value,並將每對key/value交給Mapper或者Reducer處理。表面上看來,Hadoop限定資料格式必須為key/value形式,過於簡單,很難解決複雜問題,實際上,可以通過組合的方法使key或者value(比如在key或者value中儲存多個欄位,每個欄位用分隔字元分開,或者value是個序列化後的對象,在Mapper中使用時,將其還原序列化等)儲存多重資訊,以解決輸入格式較複雜的應用。
2.2 使用者的工作
使用者編寫MapReduce需要實現的類或者方法有:
(1) InputFormat介面
使用者需要實現該介面以指定輸入檔案的內容格式。該介面有兩個方法 1 2 3 4 5 6 7 8 9 10 11 public interface InputFormat<K, V> { InputSplit[] getSplits(JobConf job, int numSplits) throws IOException; RecordReader<K, V> getRecordReader(InputSplit split, JobConf job, Reporter reporter) throws IOException; }
其中getSplits函數將所有輸入資料分成numSplits個split,每個split交給一個map task處理。getRecordReader函數提供一個使用者解析split的迭代器對象,它將split中的每個record解析成key/value對。
Hadoop本身提供了一些InputFormat:
(2)Mapper介面
使用者需繼承Mapper介面實現自己的Mapper,Mapper中必須實現的函數是 1 2 3 4 5 6 7 8 9 void map(K1 key, V1 value, OutputCollector<K2,V2> output, Reporter reporter ) throws IOException
其中,<K1 V1>是通過Inputformat中的RecordReader對象解析處理 的,OutputCollector擷取map()的輸出結果,Reporter儲存了當前task處理進度。
Hadoop本身提供了一些Mapper供使用者使用:
(3)Partitioner介面
使用者需繼承該介面實現自己的Partitioner以指定map task產生的key/value對交給哪個reduce task處理,好的Partitioner能讓每個reduce task處理的資料相近,從而達到負載平衡。Partitioner中需實現的函數是
getPartition( K2 key, V2 value, int numPartitions)
該函數返回<K2 V2>對應的reduce task ID。
使用者如果不提供Partitioner,Hadoop會使用預設的(實際上是個hash函數)。
(4)Combiner
Combiner使得map task與reduce task之間的資料轉送量大大減小,可明顯提高效能。大多數情況下,Combiner與Reducer相同。
(5)Reducer介面
使用者需繼承Reducer介面實現自己的Reducer,Reducer中必須實現的函數是 1 2 3 4 5 6 7 8 9 void reduce(K2 key, Iterator<V2> values, OutputCollector<K3,V3> output, &nbs