Hive調優及優化的12種方式
請記住:在數據處理中,不怕數據量大,就怕數據傾斜!
針對於Hive內部調優的一些方式
01.請慎重使用COUNT(DISTINCT col);
原因:
distinct會將b列所有的數據保存到內存中,形成一個類似hash的結構,速度是十分的塊;但是在大數據背景下,因為b列所有的值都會形成以key值,極有可能發生OOM
解決方案:
所以,可以考慮使用Group By 或者 ROW_NUMBER() OVER(PARTITION BY col)方式代替COUNT(DISTINCT col)
02.小文件會造成資源的多度占用以及影響查詢效率
原因:
- 眾所周知,小文件在HDFS中存儲本身就會占用過多的內存空間,那么對於MR查詢過程中過多的小文件又會造成啟動過多的Mapper Task, 每個Mapper都是一個后台線程,會占用JVM的空間
- 在Hive中,動態分區會造成在插入數據過程中,生成過多零碎的小文件(請回憶昨天講的動態分區的邏輯)
- 不合理的Reducer Task數量的設置也會造成小文件的生成,因為最終Reducer是將數據落地到HDFS中的
解決方案:
在數據源頭HDFS中控制小文件產生的個數,比如
- 采用Sequencefile作為表存儲格式,不要用textfile,在一定程度上可以減少小文件(常見於在流計算的時候采用Sequencefile格式進行存儲)
- 減少reduce的數量(可以使用參數進行控制)
- 慎重使用動態分區,最好在分區中指定分區字段的val值
- 最好數據的校驗工作,比如通過腳本方式檢測hive表的文件數量,並進行文件合並
- 合並多個文件數據到一個文件中,重新構建表
03.請慎重使用SELECT *
原因:
在大數據量多字段的數據表中,如果使用 SELECT * 方式去查詢數據,會造成很多無效數據的處理,會占用程序資源,造成資源的浪費
解決方案:
在查詢數據表時,指定所需的待查字段名,而非使用 * 號
04.不要在表關聯后面加WHERE條件
原因:
比如以下語句:
SELECT * FROM stu as t
LEFT JOIN course as t1
ON t.id=t2.stu_id
WHERE t.age=18;
請思考上面語句是否具有優化的空間?如何優化?
解決方案:
采用謂詞下推的技術,提早進行過濾有可能減少必須在數據庫分區之間傳遞的數據量
謂詞下推的解釋:
所謂謂詞下推就是通過嵌套的方式,將底層查詢語句盡量推到數據底層去過濾,這樣在上層應用中就可以使用更少的數據量來查詢,這種SQL技巧被稱為謂詞下推(Predicate pushdown)
那么上面語句就可以采用這種方式來處理:
SELECT * FROM (SELECT * FROM stu WHERE age=18) as t
LEFT JOIN course AS t1
on t.id=t1.stu_id
05.處理掉字段中帶有空值的數據
原因:
一個表內有許多空值時會導致MapReduce過程中,空成為一個key值,對應的會有大量的value值, 而一個key的value會一起到達reduce造成內存不足
解決方式:
1、在查詢的時候,過濾掉所有為NULL的數據,比如:
create table res_tbl as
select n.* from
(select * from res where id is not null ) n
left join org_tbl o on n.id = o.id;
2、查詢出空值並給其賦上隨機數,避免了key值為空(數據傾斜中常用的一種技巧)
create table res_tbl as
select n.* from res n
full join org_tbl o on
case when n.id is null then concat('hive', rand()) else n.id end = o.id;
06.設置並行執行任務數
通過設置參數 hive.exec.parallel 值為 true,就可以開啟並發執行。不過,在共享集群中,需要注意下,如果 job 中並行階段增多,那么集群利用率就會增加。
set hive.exec.parallel=true; //打開任務並行執行
set hive.exec.parallel.thread.number=16; //同一個 sql 允許最大並行度,默認為 8
07.設置合理的Reducer個數
原因:
- 過多的啟動和初始化 reduce 也會消耗時間和資源
- 有多少個Reduer就會有多少個文件產生,如果生成了很多個小文件,那么如果這些小文件作為下一個任務的輸入,則也會出現小文件過多的問題
解決方案:
Reducer設置的原則:
每個Reduce處理的數據默認是256MB
hive.exec.reducers.bytes.per.reducer=256000000
每個任務最大的reduce數,默認為1009
hive.exec.reducers.max=1009
計算reduce數的公式
N=min(參數2,總輸入數據量/參數1)
設置Reducer的數量
set mapreduce.job.reduces=n
08.JVM重用
JVM重用是Hadoop中調優參數的內容,該方式對Hive的性能也有很大的幫助,特別對於很難避免小文件的場景或者Task特別多的場景,這類場景大數據書執行時間都很短
Hadood的默認配置通常是使用派生JVM來執行map和reduce任務的,會造成JVM的啟動過程比較大的開銷,尤其是在執行Job包含有成百上千個task任務的情況。
JVM重用可以使得JVM實例在同一個job中重新使用N次,N的值可以在hadoop的mapred-site.xml文件中進行設置
<property>
<name>mapred.job.reuse.jvm.num.tasks</name>
<value>10</value>
</property>
09.為什么任務執行的時候只有一個reduce?
原因:
- 使用了Order by (Order By是會進行全局排序)
- 直接COUNT(1),沒有加GROUP BY,比如:
- SELECT COUNT(1) FROM tbl WHERE pt=’201909’
- 有笛卡爾積操作
解決方案:
避免使用全局排序,可以使用sort by進行局部排序
使用GROUP BY進行統計,不會進行全局排序,比如:
SELECT pt,COUNT(1) FROM tbl WHERE pt=’201909’;
10.選擇使用Tez引擎
Tez: 是基於Hadoop Yarn之上的DAG(有向無環圖,Directed Acyclic Graph)計算框架。它把Map/Reduce過程拆分成若干個子過程,同時可以把多個Map/Reduce任務組合成一個較大的DAG任務,減少了Map/Reduce之間的文件存儲。同時合理組合其子過程,也可以減少任務的運行時間
設置hive.execution.engine = tez;
通過上述設置,執行的每個HIVE查詢都將利用Tez
當然,也可以選擇使用spark作為計算引擎
11.選擇使用本地模式
有時候Hive處理的數據量非常小,那么在這種情況下,為查詢出發執行任務的時間消耗可能會比實際job的執行時間要長,對於大多數這種情況,hive可以通過本地模式在單節點上處理所有任務,對於小數據量任務可以大大的縮短時間
可以通過
hive.exec.mode.local.auto=true
12.選擇使用嚴格模式
Hive提供了一種嚴格模式,可以防止用戶執行那些可能產生意想不到的不好的影響查詢
比如:
- 對於分區表,除非WHERE語句中含有分區字段過濾條件來限制數據范圍,否則不允許執行,也就是說不允許掃描所有分區
- 使用ORDER BY 語句進行查詢是,必須使用LIMIT語句,因為ORDER BY 為了執行排序過程會將所有結果數據分發到同一個reduce中進行處理,強制要求用戶添加LIMIT可以防止reducer額外的執行很長時間
嚴格模式的配置:
Hive.mapred.mode=strict