Hive是Hadoop的子項(xiàng)目,它提供了對(duì)數(shù)據(jù)的結(jié)構(gòu)化管理和類SQL語言的查詢功能聋袋。SQL的交互方式極大程度地降低了Hadoop生態(tài)環(huán)境中數(shù)據(jù)處理的門檻队伟,用戶不需要編寫程序,通過SQL語句就可以對(duì)數(shù)據(jù)進(jìn)行分析和處理幽勒。目前很多計(jì)算需求都可以由Hive來完成嗜侮,極大程度地降低了開發(fā)成本。
目前啥容,Hive底層使用MapReduce作為實(shí)際計(jì)算框架锈颗,SQL的交互方式隱藏了大部分MapReduce的細(xì)節(jié)。這種細(xì)節(jié)的隱藏在帶來便利性的同時(shí)咪惠,也對(duì)計(jì)算作業(yè)的調(diào)優(yōu)帶來了一定的難度击吱。未經(jīng)優(yōu)化的SQL語句轉(zhuǎn)化后的MapReduce作業(yè),它的運(yùn)行效率可能大大低于用戶的預(yù)期遥昧。本文我們就來分析一個(gè)簡(jiǎn)單語句的優(yōu)化過程姨拥。
日常統(tǒng)計(jì)場(chǎng)景中,我們經(jīng)常會(huì)對(duì)一段時(shí)期內(nèi)的字段進(jìn)行消重并統(tǒng)計(jì)數(shù)量渠鸽,SQL語句類似于
SELECT COUNT( DISTINCT id ) FROM TABLE_NAME WHERE ...;
這條語句是從一個(gè)表的符合WHERE條件的記錄中統(tǒng)計(jì)不重復(fù)的id的總數(shù)。
該語句轉(zhuǎn)化為MapReduce作業(yè)后執(zhí)行示意圖如下柴罐,圖中還列出了我們實(shí)驗(yàn)作業(yè)中Reduce階段的數(shù)據(jù)規(guī)模:
由于引入了DISTINCT徽缚,因此在Map階段無法利用combine對(duì)輸出結(jié)果消重,必須將id作為Key輸出革屠,在Reduce階段再對(duì)來自于不同Map Task凿试、相同Key的結(jié)果進(jìn)行消重,計(jì)入最終統(tǒng)計(jì)值似芝。
我們看到作業(yè)運(yùn)行時(shí)的Reduce Task個(gè)數(shù)為1那婉,對(duì)于統(tǒng)計(jì)大數(shù)據(jù)量時(shí),這會(huì)導(dǎo)致最終Map的全部輸出由單個(gè)的ReduceTask處理党瓮。這唯一的Reduce Task需要Shuffle大量的數(shù)據(jù)详炬,并且進(jìn)行排序聚合等處理,這使得它成為整個(gè)作業(yè)的IO和運(yùn)算瓶頸寞奸。
經(jīng)過上述分析后呛谜,我們嘗試顯式地增大Reduce Task個(gè)數(shù)來提高Reduce階段的并發(fā),使每一個(gè)Reduce Task的數(shù)據(jù)處理量控制在2G左右枪萄。具體設(shè)置如下:
set mapred.reduce.tasks=100
調(diào)整后我們發(fā)現(xiàn)這一參數(shù)并沒有影響實(shí)際Reduce Task個(gè)數(shù)隐岛,Hive運(yùn)行時(shí)輸出“Number of reduce tasks determined at compile time: 1”。原來Hive在處理COUNT這種“全聚合(full aggregates)”計(jì)算時(shí)瓷翻,它會(huì)忽略用戶指定的Reduce Task數(shù)聚凹,而強(qiáng)制使用1割坠。我們只能采用變通的方法來繞過這一限制。我們利用Hive對(duì)嵌套語句的支持妒牙,將原來一個(gè)MapReduce作業(yè)轉(zhuǎn)換為兩個(gè)作業(yè)彼哼,在第一階段選出全部的非重復(fù)id,在第二階段再對(duì)這些已消重的id進(jìn)行計(jì)數(shù)单旁。這樣在第一階段我們可以通過增大Reduce的并發(fā)數(shù)沪羔,并發(fā)處理Map輸出。在第二階段象浑,由于id已經(jīng)消重蔫饰,因此COUNT(*)操作在Map階段不需要輸出原id數(shù)據(jù),只輸出一個(gè)合并后的計(jì)數(shù)即可愉豺。這樣即使第二階段Hive強(qiáng)制指定一個(gè)Reduce Task篓吁,極少量的Map輸出數(shù)據(jù)也不會(huì)使單一的Reduce Task成為瓶頸。改進(jìn)后的SQL語句如下:
SELECT COUNT(*) FROM (SELECT DISTINCT id FROM TABLE_NAME WHERE … ) t;
實(shí)際運(yùn)行時(shí)蚪拦,我們發(fā)現(xiàn)Hive還對(duì)這兩階段的作業(yè)做了額外的優(yōu)化杖剪。它將第二個(gè)MapReduce作業(yè)Map中的Count過程移到了第一個(gè)作業(yè)的Reduce階段。這樣在第一階Reduce就可以輸出計(jì)數(shù)值驰贷,而不是消重的全部id盛嘿。這一優(yōu)化大幅地減少了第一個(gè)作業(yè)的Reduce輸出IO以及第二個(gè)作業(yè)Map的輸入數(shù)據(jù)量。最終在同樣的運(yùn)行環(huán)境下優(yōu)化后的語句執(zhí)行只需要原語句20%左右的時(shí)間括袒。優(yōu)化后的MapReduce作業(yè)流如下:
從上述優(yōu)化過程我們可以看出次兆,一個(gè)簡(jiǎn)單的統(tǒng)計(jì)需求,如果不理解Hive和MapReduce的工作原理锹锰,它可能會(huì)比優(yōu)化后的執(zhí)行過程多四芥炭、五倍的時(shí)間。我們?cè)诶肏ive簡(jiǎn)化開發(fā)的同時(shí)恃慧,也要盡可能優(yōu)化SQL語句园蝠,提升計(jì)算作業(yè)的執(zhí)行效率。