MapReduce框架作為Hadoop發(fā)展初期的核心計(jì)算框架,為大數(shù)據(jù)處理技術(shù)飛速演進(jìn)提供了基石。在Hadoop生態(tài)圈中,MapReduce框架由于其成熟穩(wěn)定的性能,仍然是離線批處理技術(shù)的主力。以我們的北京移動(dòng)大數(shù)據(jù)集群為例,Hive、SparkSql是支撐探索性數(shù)據(jù)查詢的主要工具,其簡(jiǎn)單易懂的SQL語(yǔ)句查詢,可以使具備基礎(chǔ)數(shù)據(jù)庫(kù)管理能力的人員輕松上手,完美地支撐了實(shí)時(shí)數(shù)據(jù)查詢需求。
在我最初使用Java寫MapReduce程序之前,總有一個(gè)疑問(wèn):既然可以用SQL這么通俗易懂的語(yǔ)句直接操作數(shù)據(jù),而且不需要過(guò)多了解MapReduce執(zhí)行過(guò)程,為什么還要費(fèi)力地用Java壘代碼,去了解MapReduce的底層執(zhí)行過(guò)程。什么樣的應(yīng)用場(chǎng)景需要我們來(lái)開發(fā)MapReduce呢?
首先,Sql非常適用于處理結(jié)構(gòu)化數(shù)據(jù),對(duì)于非結(jié)構(gòu)化數(shù)據(jù)以及需要特殊函數(shù)處理的數(shù)據(jù)比如文本數(shù)據(jù),Sql則會(huì)力不從心。舉一個(gè)小例子,從海量文本數(shù)據(jù)中提取各種字符編碼并翻譯為中文,過(guò)程中還涉及自動(dòng)識(shí)別是utf-8還是ANSI亦或是其他編碼格式,這個(gè)需求用MapReduce程序?qū)崿F(xiàn)起來(lái)更為合理;另外,在處理業(yè)務(wù)邏輯較為復(fù)雜的任務(wù)時(shí),使用Sql很難實(shí)現(xiàn),其執(zhí)行效率方面也很難滿足業(yè)務(wù)需求。舉例來(lái)說(shuō),我們需要將業(yè)務(wù)日志中的域名識(shí)別為相應(yīng)的互聯(lián)網(wǎng)應(yīng)用,現(xiàn)實(shí)操作中需要分多種情況使用多重判斷進(jìn)行規(guī)則匹配,并剔除釣魚網(wǎng)站和fake url,使用SQL很難實(shí)現(xiàn)業(yè)務(wù)邏輯。再例如,使用Sql進(jìn)行多表join并疊加復(fù)雜的數(shù)學(xué)運(yùn)算時(shí),其效率也很難滿足業(yè)務(wù)需求。
在我們的機(jī)器學(xué)習(xí)工具開發(fā)過(guò)程中,為了使用原有數(shù)據(jù)建立特征向量,我們需要對(duì)原有表結(jié)構(gòu)進(jìn)行轉(zhuǎn)化,需要迭代原始數(shù)據(jù)生成具有較多特征值的特征向量。原始數(shù)據(jù)量為13億條,共13.2GB,我們嘗試使用Hive SQL進(jìn)行實(shí)現(xiàn),經(jīng)過(guò)測(cè)試,任務(wù)執(zhí)行時(shí)間過(guò)長(zhǎng)無(wú)法滿足需求。而使用MapReduce編寫兩個(gè)Job實(shí)現(xiàn)業(yè)務(wù)邏輯,同時(shí)使用哈希算法優(yōu)化字符串查詢效率,最終處理時(shí)長(zhǎng)為15分鐘。應(yīng)對(duì)這些復(fù)雜情況,使用MapReduce編程可以使我們獲得更多對(duì)程序?qū)崿F(xiàn)的控制和方法選擇,通過(guò)底層算法優(yōu)化實(shí)現(xiàn)效率提升。
基于不同的業(yè)務(wù)場(chǎng)景,結(jié)合不同工具特點(diǎn),我們采用SQL腳本和MapReduce開發(fā)程序結(jié)合的策略,使日常數(shù)據(jù)處理任務(wù)在效率上得到了很好地滿足。在我們平臺(tái)中,MapReduce程序承擔(dān)了如關(guān)鍵字提取、應(yīng)用匹配和標(biāo)簽規(guī)則運(yùn)算等近30%的日常數(shù)據(jù)處理任務(wù)。
總之,我們?cè)趯?shí)際應(yīng)用中依據(jù)靈活性和效率來(lái)選擇是否自己開發(fā)程序。
概覽MapReduce
認(rèn)識(shí)MapReduce先從架構(gòu)入手,在此我們一圖以蔽之:

圖 1
現(xiàn)在廣泛使用的MapReduce v2基于YARN架構(gòu),其角色包括Resource Manager(RM)、NodeManager(NM)、Application Master(AM)。RM由Master主機(jī)承擔(dān),主要負(fù)責(zé)任務(wù)調(diào)度和資源調(diào)配,NM和AM由各工作節(jié)點(diǎn)Slave承擔(dān),負(fù)責(zé)任務(wù)的處理和資源讀寫,其計(jì)算單位抽象為container。MapReduce的計(jì)算流程可以抽象為Splitting、Mapping、Shuffling、Reducing階段,其中shuffling包括了Grouping、Sorting、Partitioning過(guò)程。以WordCount為例,如下圖:

圖 2
在掌握了MapReduce架構(gòu)和原理的基礎(chǔ)上,從代碼的角度認(rèn)識(shí)MapReduce才是程序員的正確打開方式。
開發(fā)MapReduce
MapReduce程序中,Map和Reduce邏輯功能分別通過(guò)擴(kuò)展Mapper類和Reducer類實(shí)現(xiàn)。具體在實(shí)現(xiàn)過(guò)程中,我們?cè)谥黝愔袑apper和Reducer類擴(kuò)展并作為內(nèi)部類調(diào)用,最后通過(guò)main函數(shù)定義輸入輸出以及Job配置,從而作為程序主入口。
Map實(shí)現(xiàn)
Mapper類擴(kuò)展需要實(shí)現(xiàn)map方法,如下:

根據(jù)需求可以擴(kuò)展setup、cleanup和自定義方法等,擴(kuò)展Mapper類時(shí)需要聲明鍵值對(duì)類型,如 Mapper< NullWritable,Writable,IntWritable,Text >,依次分別為輸入輸出< key,value >類型,其中< NullWritable,Writable >是orc文件格式輸入< key,value >類型。
需要強(qiáng)調(diào)的是,MapReduce中所有輸入輸出字段類型都必須實(shí)現(xiàn)Writable或者WritableComparable類型,這是因?yàn)镸apReduce中磁盤讀寫和節(jié)點(diǎn)數(shù)據(jù)傳輸過(guò)程涉及到數(shù)據(jù)的序列化和反序列化,需要通過(guò)這兩類來(lái)實(shí)現(xiàn)。經(jīng)常用到的IntWritable、LongWritable、Text等都是實(shí)現(xiàn)自WritableComparable類,如果需要,我們也可以擴(kuò)展這兩類實(shí)現(xiàn)自定義數(shù)據(jù)類型。例如,在通過(guò)MapReduce實(shí)現(xiàn)兩表和多表Join的過(guò)程中,我通過(guò)實(shí)現(xiàn)WritableComparable類自定義Map輸出的key字段類型,來(lái)實(shí)現(xiàn)對(duì)于Grouping和Sorting階段不同比較字段的控制。