初見於Google Lab的Paper, http://labs.google.com/papers/mapreduce.html,論文中表明在有大量叢集支撐的情況下,可以快速的在海量文檔中進行資料處理。現在你有一堆資料,你需要按記錄修改、查詢、插入和刪除,一種辦法是你為這些記錄建立索引,比如放入資料庫,還有一種辦法就是--MapReduce。這種處理方式實際上是在資料存放的時候不建立索引,等實際處理資料的時候再將這些資料讀入記憶體進行排序,並可以用Partitioner將資料分在不同的機器上同時進行處理,因此可以方便的實現叢集計算,我猜想在一台機器上存放的資料容量以能夠全部裝進記憶體為限。
MapReduce把對資料記錄的所有操作都歸結兩個步驟--Map和Reduce。其中Map對現有資料做一個先期處理,
得到一個中間資料集,Reduce再對中間資料集進行去重、過濾等後期處理,最後得到你要的結果。Hadoop是一個MapReduce的實現,Nutch項目的大容量資料處理等功能就構建在Hadoop之上。
過程原形:
Map :: (InitialKey, IntialValue) -> [(InterKey, InterValue)]
Reduce :: (Interkey, InterValuesIterator) -> [(InterKey, InterValue)]
Map接收一個Key、Value對,返回一個Key、Value對(如果原始的Key、Value對不滿足你的要求你可以不返回,或者你有特殊需求也可以返回多個,一般比較少見), Reduce接收一個Key和一個Values的Iterator,你可以根據情況返回零個或多個Key,Value對。Key是一個實現了org.apache.hadoop.WritableComparable介面的類,Value則實現了Writable,WritableComparable是Writable的子介面,Writable定義了輸入輸出(序列化)的介面,WritableComparable另外繼承Comparable,因此Key總是有序的。
一個使用MapReduce的功能塊典型結構如下:
建立一個JobConf
設定輸入路徑
設定輸入檔案格式
設定輸入Key,Value類型
設定輸出路徑
設定輸出檔案格式
設定輸出Key,Value類型
設定Partitioner
啟動Job
執行完畢,你所有存放在輸入路徑下的資料都在被轉換之後按照你指定的格式存放於輸出路徑中,
ok,我知道很多人都是看code比看document更興奮,很不幸,我也是其中一員。
. 排序
比如你有一批URL,存放在文字檔c:/tmp/tmpin/urllist.txt中,一行一個
http://www.sohu.com
http://www.163.com
http://www.sina.com.cn
...
輸入格式一般是SequenceFileInputFormat,但是對於urllist.txt這種按行排列的格式卻是另外一種(我不知道的)格式,簡單來說就是你不需要設定輸入格式就能處理它了,在MapReduce傳遞給你的Map時,你可以忽略Key值,
而Value就是URL了,然後你做一個轉換把Value(URL)作為Key來存放,然後你自己合成一個Value值,比如只是簡單的一個數字1,或者是一個URL的相關資訊,DocumentId, 預計抓取時間等。因為這次的任務是排序,而事實上經過Map處理後資料已經是按Key(URL)排好序了,所以Reduce可以什麼也不做。
public class Main {
public static class InjectMapper extends MapReduceBase implements Mapper {
public void map(WritableComparable key, Writable val,
OutputCollector output, Reporter reporter) throws IOException {
UTF8 url = (UTF8) val;
UTF8 v = new UTF8("1");
output.collect(url, v); //產生資料
}
}
public static void main(String[] args) throws IOException {
JobConf job = new JobConf();
Path urlsPath = new Path("C:/tmp/tmpin");
job.setInputPath(urlsPath);
Path outputPath = new Path("c:/tmp/tmpout");
job.setOutputPath(outputPath);
job.setOutputFormat(SequenceFileOutputFormat.class);
job.setOutputKeyClass(UTF8.class);
job.setOutputValueClass(UTF8.class);
job.setMapperClass(InjectMapper.class);
//job.setReduceClass(InjectReducer.class);
JobClient.runJob(job);
}
}
. 去重
你的原始URL列表中可能有相同的URL,去除相同Key值記錄的功能就需要Reduce了,定義一個類
public static class InjectReducer extends MapReduceBase implements Reducer {
public void reduce(WritableComparable key, Iterator values,
OutputCollector output, Reporter reporter) throws IOException {
output.collect(key, (Writable) values.next());
}
}
並傳遞給JobConf就Ok了,由此可以看出Reduce過程對於同一個Key只被調用一次,那個values的Iterator包含這個Key所對應的全部記錄,你可以簡單的只取第一條資料,對這些記錄進行比較,得到你認為最有效一條記錄,或者統計每一個Key都有多少條記錄,一句話,你可以做任何事情(事實上是任何MapReduce所支援的事情)。