1. mapreduce程序介紹:
mapreduce是Google提出的一種計(jì)算框架。框架處理key、value對;數(shù)據(jù)處理過程中數(shù)據(jù)流向格式。
mapreduce程序開發(fā)包含三個(gè)部分:
(1)map:分布到很多節(jié)點(diǎn)上去執(zhí)行;
(2)reduce:合并map的輸出結(jié)果
(3)Job: job調(diào)度配置。
Input --> ?Map ?--> ?Reduce --> Output
2. wordcount思路:
假設(shè)對下邊這個(gè)文件wc.txt,進(jìn)行詞頻統(tǒng)計(jì)(分隔符是制表符):
SF LUNA LION
LUNA NEC LION
SF SF POM
將該文件上傳到 HDFS 上:
$cd $HADOOP_HOME
$bin/hdfs dfs -put /home/natty.ma/bigdata/hadoop/files/wordcountfiles.txt /user/natty.ma/hdfsapi/
$bin/hdfs dfs -text /user/natty.ma/hdfsapi/wordcountfiles.txt
(1)在Input階段,MapReduce內(nèi)部的FileInputFormat會(huì)將文件處理成“<偏移量,本行內(nèi)容>”這種key value的格式。
那么Input階段會(huì)生成下邊的結(jié)果:
<13,SF LUNA LION>?
<26,LUNA NEC LION>
<37,SF SF POM>
(2)在Map階段,輸入是第(1)步的輸出。將每行記錄內(nèi)容按照制表符拆分出單詞,并把拆分出的單詞作為key,value給1。
這樣得到的key value對是:
<SF,1>
<LUNA,1>
<LION,1>
<LUNA,1>
...
(3)在Reduce階段,合并每個(gè)key的多個(gè)值。
在MR內(nèi)部,會(huì)將相同的Map階段的key 的value值合并成list(Iterable)。得到下面的key value對:
<LION,(1,1)>
<LUNA,(1,1)>
<NEC,(1)>
<POM,(1)>
<SF,(1,1,1)>
上邊的集合作為Reduce階段的輸入,在reduce階段,將value值的list的值合并(sum)。Reduce的輸出是:
<LION,2>
<LUNA,2>
<NEC,1>
<POM,1>
<SF,3>
3.程序?qū)崿F(xiàn):
下面是按照規(guī)范實(shí)現(xiàn)的示例程序:
上邊程序?qū)崿F(xiàn)了WordCount的邏輯。主要包含4個(gè)部分:創(chuàng)建 Mapper類、創(chuàng)建Reducer類、配置Job、提交Job
4.提煉模板:
MapReduce也是“八股文”變成模式,最終要的是分析MapReduce要實(shí)現(xiàn)的業(yè)務(wù)邏輯,從而確定Map和Reduce階段的業(yè)務(wù)邏輯。
確定Map階段的輸入輸出、Reduce階段的輸入和輸出。確定Map和Reduce的參數(shù)(LongWritable、IntWritable、Text等)。
提煉了模板需要修改以下地方:
(1)Mapper和Reducer的類名(根據(jù)業(yè)務(wù)實(shí)際含義)
(2)Mapper類的輸入、輸出類型(輸入的key、value;輸出的key、value)。
(3)Reducer類的輸入、輸出類型(輸入的key、value;輸出的key、value)。
(4)Mapper類重寫的map方法輸入類型。map()方法的前2個(gè)參數(shù)是map的輸入key、value。輸出key、value寫入context中。
(5)Reducer類重寫的reduce方法的輸入類型。reduce()方法的前2個(gè)參數(shù)是reduce的輸入key、value。輸出key、value寫入context中。
(6)job設(shè)置mapper和reducer類 及其key、value類型。
下面是整理的模板類:
5. WebPV事例程序研究:
該事例使用一個(gè)樣本文件,以TAB鍵分割數(shù)據(jù)。數(shù)據(jù)字典:
計(jì)數(shù)每一個(gè)省份代碼(provinceId)有效的URL數(shù)量。首先,清洗每條記錄,保留合法記錄(例如,url不能為空,必須要有provinceId字段,provinceId字段需要是數(shù)字等條件)。清洗出所有合法的記錄后,統(tǒng)計(jì)每個(gè)省份代碼出現(xiàn)的次數(shù),也就是該省份PV數(shù)量了。
下面WebPV的MapReduce程序?qū)崿F(xiàn)了上述邏輯:
6. Shuffle 階段:
Shuffle描述的是從Map輸出到Reduce輸入的中間過程。Shuffle包含Map端Shuffle和Reduce端Shuffle。這中間的過程是:
Input ?--> ?map()階段 --> ?map階段output --> map端Shuffle --> Reduce端Shuffle --> Reduce階段Input?
6.1 Map端Shuffle處理:
map()函數(shù)產(chǎn)生的map的Output不是直接寫到磁盤,而是在內(nèi)存中緩存并進(jìn)行預(yù)排序。每個(gè)map任務(wù)有環(huán)形內(nèi)存緩沖來保存map輸出。這個(gè)內(nèi)存空間默認(rèn)是100MB(通過mapreduce.task.io.sort.mb來配置),但是如果這個(gè)緩沖內(nèi)存占滿了,就需要往磁盤上寫了,這個(gè)過程叫做spill。但是一般情況下,達(dá)到一個(gè)閾值就會(huì)開始spill(溢出),默認(rèn)是80%,通過mapreduce.map.sort.spill.percent 來配置。 寫在運(yùn)行map的節(jié)點(diǎn)的本地,目錄有下邊屬性配置:mapreduce.cluster.local.dir。
備注:(當(dāng)緩沖區(qū)快滿時(shí),會(huì)寫到磁盤生成一個(gè)臨時(shí)文件。當(dāng)這個(gè)map task結(jié)束后,會(huì)對磁盤上這個(gè)map task產(chǎn)生的所有臨時(shí)文件進(jìn)行排序、合并,并生成最終的輸出文件,等待reduce task來拉取數(shù)據(jù)。)
Partition、Sort、Combine、Merge、Compress:
在寫入磁盤前,會(huì)將數(shù)據(jù)分為不同的分區(qū),每個(gè)分區(qū)對應(yīng)一個(gè)reducer(也就是說分配到同一個(gè)partition的數(shù)據(jù)會(huì)由同一個(gè)reducer來處理)。在同一個(gè)分區(qū)內(nèi),數(shù)據(jù)會(huì)按照key來排序,如果有combiner函數(shù)的話,也會(huì)執(zhí)行在sort的輸出結(jié)果上。Combiner函數(shù)會(huì)先對map結(jié)果進(jìn)行合并(例如sum),這樣使得臨時(shí)文件中的數(shù)據(jù)大大減少,臨時(shí)文件也就會(huì)變小,進(jìn)而減少在集群中數(shù)據(jù)轉(zhuǎn)移量,減少I/O,提高效率。由于每次緩沖器達(dá)到閾值時(shí),都會(huì)生成一個(gè)新的臨時(shí)文件,所以很可能有很多臨時(shí)文件,這些文件最終會(huì)合并成一個(gè)單獨(dú)的分區(qū)的、排序的output文件。
生成的spill文件數(shù)量如果大于3個(gè),會(huì)再次調(diào)用combiner。如果只有1個(gè)或者2個(gè)spill文件,那么combiner就不再值得使用來降低map output文件的大小,所以combiner就不再執(zhí)行了。
在map output寫入磁盤前,做壓縮可以優(yōu)化性能。文件壓縮后變小,寫入磁盤更快,文件在拷貝到其他reducer的節(jié)點(diǎn)的時(shí)間也會(huì)縮短。默認(rèn)是不做壓縮的。可以修改屬性 :mapreduce.map.output.compress
6.2 Reduce端Shuffle處理:
1.Copy階段:
Map階段的mapoutput放在執(zhí)行map任務(wù)的機(jī)器的本地目錄(local dir)上。如果mapoutput分了多個(gè)partition。假如,mapoutput輸出文件中分區(qū)到partition1的數(shù)據(jù),就需要拷貝到reducer1執(zhí)行所在的機(jī)器。并且,reducer1需要的文件,會(huì)來自多個(gè)不同的map tasks(同時(shí)也就來自多個(gè)節(jié)點(diǎn)),這些map tasks完成時(shí)間不同,只要map task完成,reducer就會(huì)立即copy數(shù)據(jù)。
Reduce Task在copy data時(shí),如果數(shù)據(jù)較小會(huì)直接copy在內(nèi)存中,如果超過閾值,存在磁盤上。當(dāng)拷貝在磁盤上累積時(shí),后臺程序會(huì)做merge和sort。(當(dāng)然,這個(gè)是在同一個(gè)Reduce Task中做的處理,也肯定是同一個(gè)分區(qū)的數(shù)據(jù))
2. Sort階段:
map outputs拷貝完成后,進(jìn)入sort階段。該階段合并來自不同 map tasks的 map outputs并保持排序。Sort階段分多輪進(jìn)行 ,假如有50個(gè)map outputs,并且 merge參數(shù)設(shè)為10(通過mapreduce.task.io.sort.factor 屬性設(shè)定)。那么會(huì)有5個(gè)中間臨時(shí)文件。Merge階段不會(huì)把這些文件合成為一個(gè)文件傳遞給reduce階段,而是把這5個(gè)中間臨時(shí)文件直接傳給reduce,reduce階段再對這5個(gè)中間臨時(shí)文件執(zhí)行reduce函數(shù)。
3. Reduce階段:
在Reduce階段,Reduce函數(shù)在每個(gè)已排序的output文件上的每個(gè)key使用,reduce的output直接寫到HDFS。該階段進(jìn)行g(shù)roup,相同key的value組合在一起 ,例如:<hadoop,(1,1,1)>。
6.3 mapreduce優(yōu)化:
shuffle是mapreduce的核心,優(yōu)化mapreduce程序也就是優(yōu)化shuffle階段,優(yōu)化一些shuffle階段使用的參數(shù)。在優(yōu)化時(shí),可以重點(diǎn)優(yōu)化shuffle過程中的下面幾個(gè)階段:
partition
sort
combine
compress
group
7. 自定義數(shù)據(jù)類型
Writable接口(org.apache.hadoop.io.Writable):一個(gè)實(shí)現(xiàn)簡單、高效、序列化協(xié)議的序列化對象。
在MapReduce中,value必須實(shí)現(xiàn)Writable接口。
在MapReduce中,key必須實(shí)現(xiàn)WritableComparable接口。 WritableComparable接口繼承Writable和Comparable<T>。
下面的類自定義了數(shù)據(jù)類型 CustomDataWritable。包含5個(gè)屬性:上下行數(shù)據(jù)包、上下行流量、電話號碼個(gè)數(shù)計(jì)數(shù)。
8. 手機(jī)流量案例統(tǒng)計(jì)
手機(jī)流量案例(日志文件),數(shù)據(jù)字典:
如上邊數(shù)據(jù)文件的數(shù)據(jù)字典,統(tǒng)計(jì)每一個(gè)手機(jī)號的 上行數(shù)據(jù)包總數(shù)、下行數(shù)據(jù)包總數(shù)、上行總流量數(shù)、下行總流量數(shù)。?
那么其實(shí)思路非常清晰,我們篩選出符合條件的數(shù)據(jù)記錄。按照手機(jī)號碼(msisdn)來sum上邊4個(gè)指標(biāo)就可以了。非常簡單! 在用mapreduce來實(shí)現(xiàn)時(shí),我們需要?jiǎng)?chuàng)建一個(gè)“自定義數(shù)據(jù)類型” , 這個(gè)類型包含以下四個(gè)屬性:
upPackNum;
downPackNum;
upPayLoad;
downPayLoad;
這樣一簡化之后,我們就可以按照key(手機(jī)號碼),Reduce我們的自定義數(shù)據(jù)類型就可以了。
下面程序?qū)崿F(xiàn)了按手機(jī)號統(tǒng)計(jì)流量的邏輯(最后一位是該手機(jī)號出現(xiàn)的次數(shù)):