1、鏈接多個MapReduce作業
- 通常會存在這樣的情況,無法把整個流程寫在單個MapReduce作業中。因此,需要將多個MapReduce程序鏈接成更大的作業
- 數據處理可能涉及多個數據集,因此需要討論多個數據集的各種聯結技術
1-1、順序鏈接MapReduce作業
生成一個自動化的執行序列,將MapReduce作業按照順序鏈接在一起,用一個MapReduce作業的輸出作爲下一個的輸入
類似於Linux中的管道命令:
mapreduce1 | mapreduce2 | mapreduce3 | 。。。
順序鏈接MapReduce作業很簡單, driver類爲作業創建一個帶有配置參數的job(jobconf舊API)的對象,並將該對象傳遞給job.submit(jonClient.runJob舊API)來啓動這個作業;當運行到作業結尾處被阻止時,作業的鏈接會在MapReduce作業之後調用另一個作業的driver。(每個作業的driver必須創建一個新的對象,並將輸入路徑設置爲前一個輸出路徑,可以在最後一個作業中刪除前面的中間結果)
1-2、複雜依賴MapReduce作業
複雜的數據處理任務的子任務並不是按順序運行的,作業不能按照現行方式鏈接(例如,MR1處理Set1,MR2處理Set2;MR3處理MR1和MR2的結果)
通過Job和JobControl來管理這樣的非線性作業一直的相互依賴,採用的是addDependingJob()方法:
A.addDependingJob(B) //A在B完成之前不會啓動
通過addJob()方法,可以爲JobControl對象添加作業,當所有作業和依賴關係添加完成後,調用JobControl的run()方法生成一個線程來提交作業並監視其執行,JobControl中有一些方法來跟蹤批處理中各個作業的執行
1-3、預處理和後處理階段的鏈接
數據的處理任務涉及對記錄的預處理和後處理:
- 可以爲預處理和後處理步驟各自編寫一個MapReduce作業,並把它們鏈接起來。這樣的話每個步驟的中間結果都需要佔用I/O,和存儲資源,效率低下。
- 另一種方法,編寫mapper去預先調用所有的預處理步驟,再讓reducer調用所有的後處理步驟,這將強制採用模塊化和可組合的方式來構建預處理和後處理的構成【ChainMapper & ChainReducer】,全部步驟在單一的作業中運行,不會產生中間文件,大大減少I/O操作
關於2的ChainMapper & ChainReducer:
MAP | REDUCE //1
MAP+ | REDUCE |MAP* //2
MAP1 | MAP2 | REDUCE | MAP3 | MAP4 //3
- 1 的map和reduce可以重複執行一次或多次,一個跟着一個
- 2 的作業按序執行多個map來預處理,然後執行reducer 然後在執行多個map進行後處理,在ChainMapper & ChainReducer中調用addMapper()方法來分別組合預處理和後處理的步驟
- 3 的作業可以把MAP2和REDUCE視爲這個作業的核心,在他們之間使用標準的分區和洗牌操作,把MAP1視爲預處理,把MAP3,MAP4視爲後處理
示例代碼如下:
public class ChainMaper {
public static void main(String[] args) throws IOException {
Configuration conf = new Configuration();
JobConf job = new JobConf(conf);
JobConf job1 = new JobConf(false);
JobConf job2 = new JobConf(false);
JobConf job3 = new JobConf(false);
JobConf job4 = new JobConf(false);
JobConf job5 = new JobConf(false);
ChainMapper.addMapper(job, (Class<? extends Mapper<LongWritable, Text, Text, Text>>) Map1.class, LongWritable.class,Text.class,Text.class, Text.class,true,job1);
ChainMapper.addMapper(job, (Class<? extends Mapper<Text, Text, LongWritable, Text>>) Map2.class, Text.class,Text.class, LongWritable.class,Text.class,true,job2);
ChainReducer.setReducer(job, (Class<? extends Reducer<LongWritable, Text, Text, Text>>) Reduce.class, LongWritable.class,Text.class,Text.class, Text.class,true,job3);
ChainMapper.addMapper(job, (Class<? extends Mapper<Text, Text, LongWritable, Text>>) Map4.class, Text.class,Text.class, LongWritable.class,Text.class,true,job4);
ChainMapper.addMapper(job, (Class<? extends Mapper<LongWritable, Text, LongWritable, Text>>) Map5.class, LongWritable.class,Text.class, LongWritable.class,Text.class,true,job5);
JobClient.runJob(job);
}
}
2、聯結不同來源的數據
在數據分析中不可避免地需要從不同的來源提取數據。在Hadoop中數據的聯結更爲複雜,並有幾種可能的方法,需做不同的權衡
假設有如下數據記錄:(數據之間以,分隔)
客戶ID | 名字 | 電話 |
---|---|---|
1 | SS | 555 |
2 | EE | 123 |
3 | JJ | 281 |
4 | DD | 408 |
客戶ID | 訂單ID | 價格 | 交易時間 |
---|---|---|---|
3 | A | 12 | 02-Jun-2008 |
2 | B | 88 | 20-May-2008 |
3 | C | 32 | 30-Nov-2007 |
3 | D | 25 | 22-Jan-2009 |
預計的輸出結果如下:
客戶ID | 名字 | 號碼 | 訂單ID | 價錢 | 交易時間 |
---|---|---|---|---|---|
1 | SS | 555 | B | 88 | 20-May-2008 |
2 | EE | 123 | C | 32 | 30-Nov-2007 |
3 | JJ | 281 | A | 12 | 02-Jun-2008 |
3 | JJ | 281 | D | 25 | 22-Jan-2009 |
2-1、Reduce側的聯結
1、Reduce側聯結的數據流
與數據庫處理表的連接類似:
- 需要用戶自定義一個組鍵(Group Key)
- 並且爲每個記錄定義一個標籤(tag),表示其數據的來源,標籤確保這個元信息一直跟隨記錄
處理步驟如下:
- 每個map處理一個文件,在示例中有2個文件,來自2個不同的數據源
- 將每個記錄打包,使其可以在reduce側聯結
- map輸出的記錄中的鍵是用戶自定義的組鍵(group key)
- 然後進行MR框架的洗牌和排序操作
- reduce會接收到相同的組鍵(group key)的記錄,其中包括來自2個文件的數據記錄,分別帶有不同的標籤(tag)
- 輸出聯結後的記錄
Tips:存在這樣的情況,reduce處理記錄時group key1 對於tag1有一條記錄,而tag2有2條記錄時,那麼得到的結果是他們的完全交叉乘積。即group key1 tag1 tag2-1 和group key1 tag1 tag2-2 這樣的2條記錄
2、使用DATAJOIN軟件包實現聯結
Hadoop有一個名爲datajoun的軟件包,用作數據聯結的通用框架。有3個可供繼承的抽象類:
- DataJoinMapperBase
- DataJoinReducerBase
- TaggedMapOutput:是一種用Text標籤封裝記錄的數據類型,實現了getTag(),setTag(Text Tag)方法,抽象方法getData()【作爲mapper的輸出, TaggedMapOutput必須爲Writable類型,因此,必須實現readFields()和write()方法】
Datajoin軟件包已經在這些基類上實現了map()和reduce()方法,可用於執行聯結數據流。
2-2、基於DistributedCache的複製聯結
Hadoop有一種稱爲分佈式緩存的機制,設計主旨在於將文件分佈到集羣中所有節點上(通常用於分發包含所有mapper所需“背景”數據文件)
使用這個類的步驟:
- 配置作業時,調用靜態方法DistributedCache.addCacheFile()來設定要傳播到所有節點上的文件(這些文件被指定爲URI對象,除非設置了不同的文件系統,否則都保存在HDFS中)JobTracker在開始工作時,將取得URI列表,並在所有TaskTracker中創建文件的本地副本
- 在每個單獨的TaskTracker上的mapper會調用靜態方法DistributedCache.getLoalCacheFiles()來獲取數組本地副本所在的本地文件的路徑
2-3、半聯結:map側過濾後在reduce側聯結
使用複製連接的限制之一是其中一個連接表必須小到可以放在內存中,解決這個問題的方式:重新組織處理的步驟讓處理變得更爲有效。
reduce側聯結的主要問題是數據僅僅有mapper做了標籤,然後全部在網絡上重排,而它們大多數都被reducer所忽略。如果mapper在網絡重排以前,用一個額外的預過濾函數去除大多數都不必要的數據,效率低下的問題會得到改善。
3、創建一個Bloom filter
3-1、Bloom filter簡介
Bloom filter是一個數據集的摘要,它的使用讓其他的數據處理技術更爲有效。
Bloom filter對象支持2個方法:add()和contains()【存在小概率的誤報】,這2個方法的運行方式與Java中Set接口類似。