MapReduce框架學習(1)——輸入、輸出格式
- 參考: JeffreyZhou的部落格園
- 《Hadoop權威指南》第四版
在前面的學習中,完成了幾件事:
- 搭建並測試Hadoop完全分散式環境;
- 在master節點上配置Hadoop的Eclipse開發環境
上一篇博文,Eclipse的開發環境搭建中,博文最後終於揭開了WordCount的原始碼程式,這是一個小程式,但其中也包括了Map/Reduce的大體框架,這個系列博文就來捋一捋整個Map/Reduce的流程及其作用。
一個MR作業,包括三點:
- 輸入資料
- MR程式
- 配置資訊
0 Map/Reduce大致流程
- 輸入(input): 將輸入資料分成一個個split,並將spilt進一步拆成<key,value>形式;
- 對映(map):根據輸入的<key,value>進行處理,輸出list<key,value>;
- 合併(combiner):合併(單個節點上)中間相同的key值;
- 分割槽(partition):將<key,value>分成N分,分別送到下一環節;
- 化簡(reduce):將中間結果合併,得到最終結果;
- 輸出(output):指定輸出最終結果格式。
接下來我們對各個環節進行理解和應用,還是以煮爛了的栗子(WordCount)開刀:
1 .1 輸入分片與記錄
- 輸入格式(InputFormat)用於描述整個MapReduce作業的資料輸入規範。
- 先對輸入的檔案進行格式規範檢查,如輸入路徑,字尾等檢查;
- 然後對資料檔案進行輸入分塊(split),一個分片(split)就是一個由單個map操作來處理的輸入塊,每個Map操作只處理一個split;每個split被劃分若干個記錄,每個記錄就是一個<key,value>對,map一個接一個的處理記錄。
- 分片和記錄都是邏輯概念,不必對應到檔案,儘管其常見形式都是檔案。
從一般的文字檔案到資料庫,Hadoop可以處理很多不同型別的資料格式。一圖以蔽之(來源:《Hadoop權威指南》):
圖 InputFormat類的層次結構
1.2 FileInputFormat類
FIleInputFormat類是所有使用檔案作為其資料來源的InputFormat實現的基類,它提供兩個功能:
- 指出作業的輸入檔案位置(選擇作為輸入的檔案或物件);
- 為輸入檔案生成分片的程式碼實現(定義把檔案劃分到任務的InputSplits)。
- 把分片分割成記錄的作業則由其具體的子類來完成(為RecordReader讀取檔案提供了一個工程方法)。
1.3 FIleInputFormat的輸入路徑
- 提供四種靜態方法來設定job的輸入路徑:
// 單個路徑
public static void addInputPath(Job job, Path path)
// 注意,下面三個函式名多了 s,用於多個路徑
public static void addInputPaths(Job job, String commaSeparatedPaths)
public static void setInputPaths(Job job, Path... inputPaths)
public static void setInputPaths(Job job, String commaSeparatedPaths)
預設存在一個過濾器,排除隱藏檔案(名稱中以“.”和"_"開頭的檔案),也可以使用setInputPathFilter()
方法設定一個過濾器。預設的過濾器只能看到非隱藏檔案。
1.4 常用輸入格式
當然,最常用的還是兩種:
- TextInputFormat:系統預設的資料輸入格式。將檔案分塊,並逐行讀入,沒一行記錄成為一對<key,value>,其中,key為當前行在整個檔案中的位元組偏移量,LongWritable型別,value為這一行的文字內容,不包括任何行終止符(換行和回車符),它被打包成Text物件。
- KeyValueTextInputFormat:通常情況下,檔案中的鍵值對形式並非以位元組偏移量表示(用處不大),一般是Text形式的key,使用某個分界符進行分隔,例如Hadoop預設的OutputFormat產生的TextOutputFormat就是這種形式,此時用KeyValueTextInputFormat處理比較合適。
輸入格式 | 描述 | 鍵 | 值 |
---|---|---|---|
TextInputFormat | 預設格式,讀取檔案的行 | 行的位元組偏移量 | 行的內容 |
KeyValueInputFormat | 把行解析為鍵值對 | 第一個tab字元前的所有字元 | 行剩下內容 |
還是煮個栗子來的比較實在,如下兩個檔案:
其中第二個檔案,以製表符分割。
使用TextInputFormat處理第一個檔案,得到以下3條記錄:
<0, hello world, i am xiaozhou>
<27, stay hungey, stay foolish>
<54, bye game, bye boring>
看吧,在實際應用中,位元組偏移量作為key可能真的沒啥卵用。。。
使用KeyValueTextInputFormat處理第二個檔案,得到以下3條記錄:
<one, hello world, i am xiaozhou>
<two, stay hungry, stay foolish>
<three, bye game, bye boring>
以上,就是常用的兩個inputFormat的區別。
1.5 設定輸入格式
那我們怎麼選擇使用那種輸入格式呢?很簡單,面向物件的思想,你只要呼叫你所使用的輸入格式封裝好的物件就行了。只要在job函式中呼叫
job.setInputFormatclass(MyInputFormat.class)
至於怎麼去建立自己的MyInputFormat,參照上面InputFormat類的層次結構,進行繼承和複寫就行了:
- 如果資料來源是檔案,則可以繼承FIleInputFormat:
public class MyInputFormat extends FileInputFormat<Text,Text> {
@Override
public RecordReader<Text, Text> createRecordReader(InputSplit split,
TaskAttemptContext context) throws IOException, InterruptedException {
// TODO Auto-generated method stub
return null;
}
}
- 如果資料來源是非檔案,如關係資料,則繼承:
public class MyInputFormat extends InputFormat<Text,Text> {
// 將spilt輸出成<key,value>
@Override
public RecordReader<Text, Text> createRecordReader(InputSplit arg0,
TaskAttemptContext arg1) throws IOException, InterruptedException {
// TODO Auto-generated method stub
return null;
}
// 拆分為spilt
@Override
public List<InputSplit> getSplits(JobContext arg0) throws IOException,
InterruptedException {
// TODO Auto-generated method stub
return null;
}
}
1.6 輸出格式
資料輸出格式(OutputFormat)用於描述MR作業的資料輸出規範,Hadoop提供了豐富的內建資料輸出格式。最常的資料輸出格式是TextOutputFormat,也是系統預設的資料輸出格式,將結果以"key+\t+value"的形式逐行輸出到文字檔案中。還有其它的,如來源:《Hadoop權威指南》:
1.7 設定輸出格式
預設的輸出格式是TextOutputFormat,把每條記錄寫為文字行,鍵值可以是任意型別,因為TextOutputFormat會呼叫toString()方法把它們轉換為字串,每個鍵值對由製表符(tab)進行分隔(當然也可以設定分隔符),與其對應的輸入格式是KeyValueOutputFormat。
若要自定義輸出格式,如下:
public class MyOutputFormat extends OutputFormat<Text,Text> {
@Override
public void checkOutputSpecs(JobContext arg0)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
}
@Override
public OutputCommitter getOutputCommitter(TaskAttemptContext arg0)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
return null;
}
@Override
public RecordWriter<Text, Text> getRecordWriter(TaskAttemptContext arg0)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
return null;
}
}
1.8 複合鍵
從前面的整個過程中可以看到,都是採用key-value的方式進行傳入傳出,而這些key或者value型別大多是單一的字串或者整型,也就是基本資料型別。如果我的key中需要包含多個資訊怎麼辦?用字串直接拼接麼? 太不方便了,最好能夠自己定義一個類,作為這個key,這樣就方便了。
要自定義一個類作為key或value的型別,就要實現WriableComparable類,複寫其中三個函式如下:
public class MyType implements WritableComparable<MyType> {
private float x,y;
public float GetX(){return x;}
public float GetY(){return y;}
// 讀緩衝
@Override
public void readFields(DataInput in) throws IOException {
x = in.readFloat();
y = in.readFloat();
}
// 序列化
@Override
public void write(DataOutput out) throws IOException {
out.writeFloat(x);
out.writeFloat(y);
}
// 比較器
@Override
public int compareTo(MyType arg0) {
//輸入:-1(小於) 0(等於) 1(大於)
return 0;
}
}
關於複合鍵,在本節內容學習的最後,會寫一個倒排索引的程式例子,就會使用複合鍵。
注:關於writable還有很多細節上的知識,《Hadoop權威指南》上也沒系統性的講解,待後面遇到了實際問題再解決吧。
1.x 後記
目前能用到的關於檔案格式的知識大概也就這些了,看《Hadoop權威指南》上還有很多細節上的東西,等以後用到了再回來查吧,不然學了也記不住。