開發優化:
1、分區、列剪裁。
在查詢的過程中減少不必要的分區,只讀取查詢中所需要用到的列,而忽略其它列
2、謂詞下推。
SQL語句中where謂詞邏輯提前執行,減少下游處理數據量
select a,b from table_a join
(select a,b from table_b where log_date='20200323') table_c
on table_a.a = table_c.a
先從table中挑選出參與計算的數據,指定分區,指定列,減少後續處理數據量
3、兩表join,小表在前
在 Reduce 階段,位於 Join 操作符左邊的表的內容會被加載進內存,載入條目較少的表 可以有效減少 OOM(out of memory)即內存溢出。所以對於同一個 key 來說,對應的 value 值小的放前,大的放後,這便是“小表放前”原則。 若一條語句中有多個 Join,依據 Join 的條件相同與否,有不同的處理方法。如果 Join 的 key 相同,不管有多少個表,都會則會合併爲一個 Map-Reduce
4、使用map join
Join 操作在 Map 階段完成,不再需要Reduce,前提條件是需要的數據在 Map 的過程中可以訪問到
insert overwrite table r
select /* + MAPJOIN(a) */ b.a1,b.a2
from a join b on a.a1=b.a1
map join的配置項是hive.auto.convert.join
,默認值true,對應邏輯優化器是MapJoinProcessor。
還有一些參數用來控制map join的行爲,比如hive.mapjoin.smalltable.filesize
,當build table大小小於該值就會啓用map join,默認值25000000(25MB)。還有hive.mapjoin.cache.numrows
,表示緩存build table的多少行數據到內存,默認值25000。
5、group by 替代distinct
當要統計某一列的去重數時,如果數據量很大,count(distinct)就會非常慢,原因與order by類似,count(distinct)邏輯只會有很少的reducer來處理。這時可以用group by來改寫
select count(1) from (
select uid from a
where log_date = 20200323
group by uid
) t;
配置調優
1、設置mapper個數
可以直接通過參數mapred.map.tasks
(默認值2)來設定mapper數的期望值,但它不一定會生效,下面會提到。
設輸入文件的總大小爲total_input_size
。HDFS中,一個塊的大小由參數dfs.block.size
指定,默認值64MB或128MB。在默認情況下,mapper數就是:default_mapper_num = total_input_size / dfs.block.size
。
參數mapred.min.split.size
(默認值1B)和mapred.max.split.size
(默認值64MB)分別用來指定split的最小和最大大小。split大小和split數計算規則是:split_size = MAX(mapred.min.split.size, MIN(mapred.max.split.size, dfs.block.size))
;split_num = total_input_size / split_size
。
得出mapper數:mapper_num = MIN(split_num, MAX(default_num, mapred.map.tasks))
。
2、設置reducer個數
reducer數量的確定方法比mapper簡單得多。使用參數mapred.reduce.tasks
可以直接設定reducer數量,不像mapper一樣是期望值。但如果不設這個參數的話,Hive就會自行推測,邏輯如下:
參數hive.exec.reducers.bytes.per.reducer
用來設定每個reducer能夠處理的最大數據量,默認值1G(1.2版本之前)或256M(1.2版本之後)。
參數hive.exec.reducers.max
用來設定每個job的最大reducer數量,默認值999(1.2版本之前)或1009(1.2版本之後)。
得出reducer數:reducer_num = MIN(total_input_size / reducers.bytes.per.reducer, reducers.max)
。
reducer數量與輸出文件的數量相關。如果reducer數太多,會產生大量小文件,對HDFS造成壓力。如果reducer數太少,每個reducer要處理很多數據,容易拖慢運行時間或者造成OOM。
3、合併小文件
輸入階段合併需要更改Hive的輸入文件格式,即參數hive.input.format
,默認值是org.apache.hadoop.hive.ql.io.HiveInputFormat
,我們改成org.apache.hadoop.hive.ql.io.CombineHiveInputFormat
。
這樣比起上面調整mapper數時,又會多出兩個參數,分別是mapred.min.split.size.per.node
和mapred.min.split.size.per.rack
,含義是單節點和單機架上的最小split大小。如果發現有split大小小於這兩個值(默認都是100MB),則會進行合併。
輸出階段合併直接將hive.merge.mapfiles
和hive.merge.mapredfiles
都設爲true即可,前者表示將map-only任務的輸出合併,後者表示將map-reduce任務的輸出合併。另外,hive.merge.size.per.task
可以指定每個task輸出後合併文件大小的期望值,hive.merge.size.smallfiles.avgsize
可以指定所有輸出文件大小的均值閾值,默認值都是1GB。如果平均大小不足的話,就會另外啓動一個任務來進行合併。
4、啓用壓縮
壓縮job的中間結果數據和輸出數據,可以用少量CPU時間節省很多空間。壓縮方式一般選擇Snappy,效率最高。
要啓用中間壓縮,需要設定hive.exec.compress.intermediate
爲true,同時指定壓縮方式hive.intermediate.compression.codec
爲org.apache.hadoop.io.compress.SnappyCodec
。另外,參數hive.intermediate.compression.type
可以選擇對塊(BLOCK)還是記錄(RECORD)壓縮,BLOCK的壓縮率比較高。
輸出壓縮的配置基本相同,打開hive.exec.compress.output
即可。
5、JVM重用
在MR job中,默認是每執行一個task就啓動一個JVM。如果task非常小而碎,那麼JVM啓動和關閉的耗時就會很長。可以通過調節參數mapred.job.reuse.jvm.num.tasks
來重用。例如將這個參數設成5,那麼就代表同一個MR job中順序執行的5個task可以重複使用一個JVM,減少啓動和關閉的開銷。但它對不同MR job中的task無效。
6、並行執行
Hive中互相沒有依賴關係的job間是可以並行執行的,最典型的就是多個子查詢union all。在集羣資源相對充足的情況下,可以開啓並行執行,即將參數hive.exec.parallel
設爲true。另外hive.exec.parallel.thread.number
可以設定並行執行的線程數,默認爲8,一般都夠用。
6、嚴格模式
所謂嚴格模式,就是強制不允許用戶執行3種有風險的HiveSQL語句,一旦執行會直接失敗。這3種語句是:
-
查詢分區表時不限定分區列的語句;
-
兩表join產生了笛卡爾積的語句;
-
用order by來排序但沒有指定limit的語句。
要開啓嚴格模式,需要將參數hive.mapred.mode
設爲strict。