第一部分:重要的組件Combiner•什麼是Combiner•combine函數把一個map函數產生的<key,value>對(多個key, value)合并成一個新的<key2,value2>. 將新的<key2,value2>作為輸入到reduce函數中,其格式與reduce函數相同。•這樣可以有效較少中間結果,減少網路傳輸負荷。 •什麼情況下可以使用Combiner•可以對記錄進行匯總統計的情境,如求和。•求平均數的情境就不可以使用了Combiner執行時機•運行combiner函數的時機有可能會是merge完成之前,或者之後,這個時機可以由一個參數控制,即
min.num.spill.for.combine(default 3)•當job中設定了combiner,並且spill數最少有3個的時候,那麼combiner函數就會在merge產生結果檔案之前運行•通過這樣的方式,就可以在spill非常多需要merge,並且很多資料需要做conbine的時候,減少寫入到磁碟檔案的資料數量,同樣是為了減少對磁碟的讀寫頻率,有可能達到最佳化作業的目的。•Combiner也有可能不執行, Combiner會考慮當時叢集的負載情況。Combiner如何使用•程式碼範例•繼承Reducer類public static class Combiner extends MapReduceBase implements Reducer<Text, Text, Text, Text> { public void reduce(Text key, Iterator<Text> values, OutputCollector<Text, Text> output, Reporter reporter) throws IOException { } } •配置作業時加入conf.setCombinerClass(Combiner.class) Partitioner•什麼是Partitioner•Mapreduce 通過Partitioner 對Key 進行分區,進而把資料按我們自己的需求來分發。•什麼情況下使用Partitioner•如果你需要key按照自己意願分發,那麼你需要這樣的組件。•例如:資料檔案內包含省份,而輸出要求每個省份輸出一個檔案。•架構預設的HashPartitioner •public class HashPartitioner<K, V> extends Partitioner<K, V> {
/** Use {@link Object#hashCode()} to partition. */
public int getPartition(K key, V value,
int numReduceTasks) {
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
} Partitioner如何使用 •實現Partitioner介面覆蓋getPartition()方法 •配置作業時加入conf.setPartitionerClass(MyPartitioner.class); •Partitioner樣本 public static class MyPartitioner implements Partitioner<Text, Text> { @Override
public int getPartition(Text key, Text value, int numPartitions) { } } Partitioner需求樣本 •需求描述 •資料檔案中含有省份 •需要相同的省份送到相同的Reduce裡 •從而產生不同的檔案 •資料範例 •1 liaoning •1 代表該省份有多少個直轄市 •步驟 •實現Partitioner,覆蓋getPartition •根據省份欄位進行切分
RecordReader
•什麼是RecordReader •用於在分塊中讀取<Key,Value>對,也就是說每一次我們讀取一條記錄都會調用該類。 •主要是處理經過InputFormat分區完的資料 •什麼時候使用RecordReader •需要對輸入的資料按自己的需求處理 •如:要求輸入的key不是檔案的位移量而是檔案的路徑或者名字 •系統預設為LineRecordReader •按照每行的位移量做為map輸出時的key值,每行的內容作為map的value值,預設的分隔字元是斷行符號和換行。
RecordReader需求樣本
•需求 •更改map對應的輸入的<key,value>值,key對應的檔案的路徑(或者是檔案名稱),value對應的是檔案的內容(content)。 •步驟 •重寫InputFormat不對檔案切分 •重寫RecordReader •在配置作業時使用自訂的組件進行資料處理
第二部分:Join 案例分析 •輸入為2個檔案,檔案一內容如下 •空格分割:使用者名稱 手機號 年齡 •內容範例 •Tom 1314567890 14 •檔案二內容 •空格分割:手機號 地市 •內容範例 •13124567890 hubei •需要統計出的匯總資訊為 使用者名稱 手機號 年齡 地市 Map端Join •設計思路 •使用DistributedCache.addCacheFile()將地市的檔案加入到所有Map的緩衝裡 •在Map函數裡讀取該檔案,進行Join • 將結果輸出到reduce •需要注意的是 •DistributedCache需要在產生Job作業前使用
Reduce端Join
•設計思路 •Map端讀取所有檔案,並在輸出的內容裡加上標識代表資料時從哪個檔案裡來的 •在reduce對按照標識對資料進行儲存 •然後根據Key的Join來求出結果直接輸出
第三部分:排序
普通排序 •Mapreduce本身內建排序功能 •Text對象是不適合排序的,如果內容為整型不會安照編碼順序去排序 •一般情況下我們可以考慮以IntWritable做為Key,同時將Reduce設定成0 ,進行排序 部分排序 •即輸出的每個檔案都是排過序的 •如果我們不需要全域排序,那麼這是個不錯的選擇。
全域排序 •產生背景 •Hadoop平台沒有提供全域資料排序,而在大規模資料處理中進行資料的全域排序是非常普遍的需求。 •使用hadoop進行大量的資料排序排序最直觀的方法是把檔案所有內容給map之後,map不做任何處理,直接輸出給一個reduce,利用hadoop的自己的shuffle機制,對所有資料進行排序,而後由reduce直接輸出。 •快速排序基本步驟就是需要現在所有資料中選取一個作為支點。然後將大於這個支點的放在一邊,小於這個支點的放在另一邊。
設想如果我們有
N
個支點(這裡可以稱為尺規),就可以把所有的資料分成
N+1
個
part
,將這
N+1
個
part
丟給
reduce
,由
hadoop
自動排序,最後輸出
N+1
個內部有序的檔案,再把這
N+1
個檔案首尾相連合并成一個檔案,收工
。 由此我們可以歸納出這樣一個用
hadoop
對大量資料排序的步驟: 1
)
對待排序資料進行抽樣; 2
)
對抽樣資料進行排序,產生尺規; 3
)
Map
對輸入的每條資料計算其處於哪兩個尺規之間;將資料發給對應區間
ID
的
reduce 4
)
Reduce
將獲得資料直接輸出。 •Hadoop 提供了Sampler介面可以返回一組樣本,該介面為Hadoop的採樣器。 public interface Sampler<K, V> { K[] getSample(InputFormat<K, V> inf, Job job) throws IOException, InterruptedException; } •Hadoop提供了一個TotalOrderPartitioner,可以使我們來實現全域排序。 二次排序 •產生背景 •MapReduce預設會對key進行排序 •將輸出到Reduce的values也進行預先的排序 •實現方式 •重寫Partitioner,完成key分區,進行第一次排序 •實現WritableComparator,完成自己的排序邏輯,完成key的第2次排序 •原理 •Map之前的資料 key1 1 key2 2 key2 3 key3 4 key1 2 •Mapduce只能排序key,所以為了二次排序我們要重新定義自己的key 簡單說來就是<key value> value ,組合完後 <key1 1 > 1 <key2 2 > 2 <key2 3 > 3 <key3 4> 4 <key1 2 > 2
•原理 •接下來實現自訂的排序類,分組類,資料變成 <key1 1 > 1 <key1 2 > 2 <key2 2 > 2 <key2 3 > 3 <key3 4> 4 •最後 reduce處理後輸出結果 key1 1 key1 2 key2 2 key2 3 key3 4
第四部分:計數器 •什麼是計數器 計數器主要用來收集系統資訊和作業運行資訊,用於知道作業成功、失敗等情況,比日誌更便利進行分析。 •內建計數器 •Hadoop內建的計數器,記錄作業執行情況和記錄情況。包括MapReduce架構、檔案系統、作業計數三大類。 •計數器由關聯任務維護,定期傳遞給tasktracker,再由tasktracker傳給jobtracker。 •計數器可以被全域聚集。內建的作業計數器實際上由jobtracker維護,不必在整個網路中傳遞。 •當一個作業執行成功後,計數器的值才是完整可靠的。
使用者自訂Java計數器
•MapReduce架構允許使用者自訂計數器 •計數器是全域使用的 •計數器有組的概念,可以由一個Java枚舉類型來定義 •如何配置 •0.20.2以下的版本使用Reporter, •0.20.2以上的版本使用context.getCounter(groupName, counterName) 來擷取計數器配置並設定。 •動態計數器 •所謂動態計數器即不採用Java枚舉的方式來定義 •Reporter中的擷取動態計數器的方法 •public void incrCounter(String group,String counter,long amount) 組名稱,計數器名稱,計數值 •一些原則 •建立計數器時,盡量讓名稱易讀
•擷取計數器 •Web UI •命令列 hadoop job-counter •Java API •Java API •在作業運行完成後,計數器穩定後擷取。 使用job.getCounters()得到Counters
第五部分:合并小檔案樣本 •產生背景 •Hadoop不適合處理小檔案 •會佔用大量的記憶體空間 •解決方案 •檔案內容讀取到SequenceFile內