一、limit限制調整
通常狀況下,Limit語句仍是須要執行整個查詢語句,而後再返回部分結果。html
有一個配置屬性能夠開啓,避免這種狀況---對數據源進行抽樣node
hive.limit.optimize.enable=true --- 開啓對數據源進行採樣的功能sql
hive.limit.row.max.size --- 設置最小的採樣容量數據庫
hive.limit.optimize.limit.file --- 設置最大的採樣樣本數apache
缺點:有可能部分數據永遠不會被處理到緩存
2.JOIN優化
1). 將大表放後頭併發
Hive假定查詢中最後的一個表是大表。它會將其它表緩存起來,而後掃描最後那個表。app
所以一般須要將小表放前面,或者標記哪張表是大表:/*streamtable(table_name) */負載均衡
2). 使用相同的鏈接鍵jvm
當對3個或者更多個表進行join鏈接時,若是每一個on子句都使用相同的鏈接鍵的話,那麼只會產生一個MapReduce job。
3). 儘可能儘早地過濾數據
減小每一個階段的數據量,對於分區表要加分區,同時只選擇須要使用到的字段。
4). 儘可能原子化操做
儘可能避免一個SQL包含複雜邏輯,可使用中間表來完成複雜的邏輯
3. 本地模式
有時hive的輸入數據量是很是小的。在這種狀況下,爲查詢出發執行任務的時間消耗可能會比實際job的執行時間要多的多。對於大多數這種狀況,hive能夠經過本地模式在單臺機器上處理全部的任務。對於小數據集,執行時間會明顯被縮短
set hive.exec.mode.local.auto=true;
當一個job知足以下條件才能真正使用本地模式:
1.job的輸入數據大小必須小於參數:hive.exec.mode.local.auto.inputbytes.max(默認128MB)
2.job的map數必須小於參數:hive.exec.mode.local.auto.tasks.max(默認4)
3.job的reduce數必須爲0或者1
可用參數hive.mapred.local.mem(默認0)控制child jvm使用的最大內存數。
4.並行執行
hive會將一個查詢轉化爲一個或多個階段,包括:MapReduce階段、抽樣階段、合併階段、limit階段等。默認狀況下,一次只執行一個階段。 不過,若是某些階段不是互相依賴,是能夠並行執行的。
set hive.exec.parallel=true,能夠開啓併發執行。
set hive.exec.parallel.thread.number=16; //同一個sql容許最大並行度,默認爲8。
會比較耗系統資源。
5.strict模式
--對分區表進行查詢,在where子句中沒有加分區過濾的話,將禁止提交任務(默認:nonstrict)
set hive.mapred.mode=strict;
注:使用嚴格模式能夠禁止3種類型的查詢:
(1)對於分區表,不加分區字段過濾條件,不能執行
(2)對於order by語句,必須使用limit語句。
(3)限制笛卡爾積的查詢(join的時候不使用on,而使用where的)。
6.調整mapper和reducer個數
Map階段優化
map執行時間:map任務啓動和初始化的時間+邏輯處理的時間。
1.一般狀況下,做業會經過input的目錄產生一個或者多個map任務。
主要的決定因素有: input的文件總個數,input的文件大小,集羣設置的文件塊大小(目前爲128M, 可在hive中經過set dfs.block.size;命令查看到,該參數不能自定義修改);
2.舉例:
a)假設input目錄下有1個文件a,大小爲780M,那麼hadoop會將該文件a分隔成7個塊(6個128m的塊和1個12m的塊),從而產生7個map數
b)假設input目錄下有3個文件a,b,c,大小分別爲10m,20m,130m,那麼hadoop會分隔成4個塊(10m,20m,128m,2m),從而產生4個map數
即,若是文件大於塊大小(128m),那麼會拆分,若是小於塊大小,則把該文件當成一個塊。
3.是否是map數越多越好?
答案是否認的。若是一個任務有不少小文件(遠遠小於塊大小128m),則每一個小文件也會被當作一個塊,用一個map任務來完成,而一個map任務啓動和初始化的時間遠遠大於邏輯處理的時間,就會形成很大的資源浪費。並且,同時可執行的map數是受限的。
4.是否是保證每一個map處理接近128m的文件塊,就高枕無憂了?
答案也是不必定。好比有一個127m的文件,正常會用一個map去完成,但這個文件只有一個或者兩個小字段,卻有幾千萬的記錄,若是map處理的邏輯比較複雜,用一個map任務去作,確定也比較耗時。
針對上面的問題3和4,咱們須要採起兩種方式來解決:即減小map數和增長map數;
如何合併小文件,減小map數?
假設一個SQL任務:
Select count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’;
該任務的inputdir /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04
共有194個文件,其中不少是遠遠小於128m的小文件,總大小9G,正常執行會用194個map任務。
Map總共消耗的計算資源: SLOTS_MILLIS_MAPS= 623,020
經過如下方法來在map執行前合併小文件,減小map數:
set mapred.max.split.size=100000000;
set mapred.min.split.size.per.node=100000000;
set mapred.min.split.size.per.rack=100000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
再執行上面的語句,用了74個map任務,map消耗的計算資源: SLOTS_MILLIS_MAPS=333,500
對於這個簡單SQL任務,執行時間上可能差很少,但節省了一半的計算資源。
大概解釋一下,100000000表示100M,
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
這個參數表示執行前進行小文件合併,
前面三個參數肯定合併文件塊的大小,大於文件塊大小128m的,按照128m來分隔,
小於128m,大於100m的,按照100m來分隔,把那些小於100m的(包括小文件和分隔大文件剩下的),
進行合併,最終生成了74個塊。
如何適當的增長map數?
當input的文件都很大,任務邏輯複雜,map執行很是慢的時候,能夠考慮增長Map數,
來使得每一個map處理的數據量減小,從而提升任務的執行效率。
假設有這樣一個任務:
Select data_desc,
count(1),
count(distinct id),
sum(case when …),
sum(case when ...),
sum(…)
from a group by data_desc
若是表a只有一個文件,大小爲120M,但包含幾千萬的記錄,
若是用1個map去完成這個任務,確定是比較耗時的,
這種狀況下,咱們要考慮將這一個文件合理的拆分紅多個,
這樣就能夠用多個map任務去完成。
set mapred.reduce.tasks=10;
create table a_1 as
select * from a
distribute by rand(123);
這樣會將a表的記錄,隨機的分散到包含10個文件的a_1表中,再用a_1代替上面sql中的a表,則會用10個map任務去完成。
每一個map任務處理大於12M(幾百萬記錄)的數據,效率確定會好不少。
看上去,貌似這兩種有些矛盾,一個是要合併小文件,一個是要把大文件拆成小文件,這點正是重點須要關注的地方,
根據實際狀況,控制map數量須要遵循兩個原則:使大數據量利用合適的map數;使單個map任務處理合適的數據量;
2、控制hive任務的reduce數:
1.Hive本身如何肯定reduce數:
reduce個數的設定極大影響任務執行效率,不指定reduce個數的狀況下,Hive會猜想肯定一個reduce個數,基於如下兩個設定:
hive.exec.reducers.bytes.per.reducer(每一個reduce任務處理的數據量,默認爲1000^3=1G)
hive.exec.reducers.max(每一個任務最大的reduce數,默認爲999)
計算reducer數的公式很簡單N=min(參數2,總輸入數據量/參數1)
即,若是reduce的輸入(map的輸出)總大小不超過1G,那麼只會有一個reduce任務;
如:
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;
/group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04 總大小爲9G多,
所以這句有10個reduce
2.調整reduce個數方法一:
調整hive.exec.reducers.bytes.per.reducer參數的值;
set hive.exec.reducers.bytes.per.reducer=500000000; (500M)
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt; 此次有20個reduce
3.調整reduce個數方法二;
set mapred.reduce.tasks = 15;
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;此次有15個reduce
4.reduce個數並非越多越好;
同map同樣,啓動和初始化reduce也會消耗時間和資源;
另外,有多少個reduce,就會有多少個輸出文件,若是生成了不少個小文件,
那麼若是這些小文件做爲下一個任務的輸入,則也會出現小文件過多的問題;
5.什麼狀況下只有一個reduce;
不少時候你會發現任務中無論數據量多大,無論你有沒有設置調整reduce個數的參數,任務中一直都只有一個reduce任務;
其實只有一個reduce任務的狀況,除了數據量小於hive.exec.reducers.bytes.per.reducer參數值的狀況外,還有如下緣由:
a)沒有group by的彙總,好比把select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;
寫成 select count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04';
這點很是常見,但願你們儘可能改寫。
b)用了Order by
c)有笛卡爾積
一般這些狀況下,除了找辦法來變通和避免,我暫時沒有什麼好的辦法,
由於這些操做都是全局的,因此hadoop不得不用一個reduce去完成;
一樣的,在設置reduce個數的時候也須要考慮這兩個原則:
使大數據量利用合適的reduce數;使單個reduce任務處理合適的數據量。
2 Reduce階段優化
調整方式:
-- set mapred.reduce.tasks=?
-- set hive.exec.reducers.bytes.per.reducer = ?
通常根據輸入文件的總大小,用它的estimation函數來自動計算reduce的個數:reduce個數 = InputFileSize / bytes per reducer
7.JVM重用
--用於避免小文件的場景或者task特別多的場景,這類場景大多數執行時間都很短,由於hive調起mapreduce任務,JVM的啓動過程會形成很大的開銷,尤爲是job有成千上萬個task任務時,JVM重用可使得JVM實例在同一個job中從新使用N次
set mapred.job.reuse.jvm.num.tasks=10; --10爲重用個數
8.動態分區調整
--動態分區屬性:設置爲true表示開啓動態分區功能(默認爲false)
hive.exec.dynamic.partition=true;
--動態分區屬性:設置爲nonstrict,表示容許全部分區都是動態的(默認爲strict)
--設置爲strict,表示必須保證至少有一個分區是靜態的
hive.exec.dynamic.partition.mode=strict;
--動態分區屬性:每一個mapper或reducer能夠建立的最大動態分區個數
hive.exec.max.dynamic.partitions.pernode=100;
--動態分區屬性:一個動態分區建立語句能夠建立的最大動態分區個數
hive.exec.max.dynamic.partitions=1000;
--動態分區屬性:全局能夠建立的最大文件個數
hive.exec.max.created.files=100000;
--控制DataNode一次能夠打開的文件個數
--這個參數必須設置在DataNode的$HADOOP_HOME/conf/hdfs-site.xml文件中
<property>
<name>dfs.datanode.max.xcievers</name>
<value>8192</value>
</property>
9.推測執行
--目的:是經過加快獲取單個task的結果以及進行偵測將執行慢的TaskTracker加入到黑名單的方式來提升總體的任務執行效率
(1)修改 $HADOOP_HOME/conf/mapred-site.xml文件
<property>
<name>mapred.map.tasks.speculative.execution </name>
<value>true</value>
</property>
<property>
<name>mapred.reduce.tasks.speculative.execution </name>
<value>true</value>
</property>
(2)修改hive配置
set hive.mapred.reduce.tasks.speculative.execution=true;
10.數據傾斜
表現:任務進度長時間維持在99%(或100%),查看任務監控頁面,發現只有少許(1個或幾個)reduce子任務未完成。由於其處理的數據量和其餘reduce差別過大。
單一reduce的記錄數與平均記錄數差別過大,一般可能達到3倍甚至更多。 最長時長遠大於平均時長。
緣由
1)、key分佈不均勻
2)、業務數據自己的特性
3)、建表時考慮不周
4)、某些SQL語句自己就有數據傾斜
關鍵詞 |
情形 |
後果 |
join |
其中一個表較小,可是key集中 |
分發到某一個或幾個Reduce上的數據遠高於平均值 |
join |
大表與大表,可是分桶的判斷字段0值或空值過多 |
這些空值都由一個reduce處理,灰常慢 |
group by |
group by 維度太小,某值的數量過多 |
處理某值的reduce灰常耗時 |
count distinct |
某特殊值過多 |
處理此特殊值reduce耗時 |
解決方案:
參數調節
hive.map.aggr=true
11. 其餘參數調優
--開啓CLI提示符前打印出當前所在的數據庫名
set hive.cli.print.current.db=true;
--讓CLI打印出字段名稱
hive.cli.print.header=true;
--設置任務名稱,方便查找監控
SET mapred.job.name=P_DWA_D_IA_S_USER_PROD;
--決定是否能夠在 Map 端進行聚合操做
set hive.map.aggr=true;
--有數據傾斜的時候進行負載均衡
set hive.groupby.skewindata=true;
--對於簡單的不須要聚合的相似SELECT <col> from <table> LIMIT n語句,不須要起MapReduce job,直接經過Fetch task獲取數據
set hive.fetch.task.conversion=more;
十二、小文件問題
小文件是如何產生的
1.動態分區插入數據,產生大量的小文件,從而致使map數量劇增。
2.reduce數量越多,小文件也越多(reduce的個數和輸出文件是對應的)。
3.數據源自己就包含大量的小文件。
小文件問題的影響
1.從Hive的角度看,小文件會開不少map,一個map開一個JVM去執行,因此這些任務的初始化,啓動,執行會浪費大量的資源,嚴重影響性能。
2.在HDFS中,每一個小文件對象約佔150byte,若是小文件過多會佔用大量內存。這樣NameNode內存容量嚴重製約了集羣的擴展。
小文件問題的解決方案
從小文件產生的途經就能夠從源頭上控制小文件數量,方法以下:
1.使用Sequencefile做爲表存儲格式,不要用textfile,在必定程度上能夠減小小文件。
2.減小reduce的數量(可使用參數進行控制)。
3.少用動態分區,用時記得按distribute by分區。
對於已有的小文件,咱們能夠經過如下幾種方案解決:
1.使用hadoop archive命令把小文件進行歸檔。
2.重建表,建表時減小reduce數量。
3.經過參數進行調節,設置map/reduce端的相關參數,以下:
設置map輸入合併小文件的相關參數:
//每一個Map最大輸入大小(這個值決定了合併後文件的數量)
set mapred.max.split.size=256000000;
//一個節點上split的至少的大小(這個值決定了多個DataNode上的文件是否須要合併)
set mapred.min.split.size.per.node=100000000;
//一個交換機下split的至少的大小(這個值決定了多個交換機上的文件是否須要合併)
set mapred.min.split.size.per.rack=100000000;
//執行Map前進行小文件合併
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
設置map輸出和reduce輸出進行合併的相關參數:
//設置map端輸出進行合併,默認爲true
set hive.merge.mapfiles = true
//設置reduce端輸出進行合併,默認爲false
set hive.merge.mapredfiles = true
//設置合併文件的大小
set hive.merge.size.per.task = 256*1000*1000
//當輸出文件的平均大小小於該值時,啓動一個獨立的MapReduce任務進行文件merge。
set hive.merge.smallfiles.avgsize=16000000
設置以下參數取消一些限制(HIVE 0.7後沒有此限制):
hive.merge.mapfiles=false
默認值:true
描述:是否合併Map的輸出文件,也就是把小文件合併成一個map
hive.merge.mapredfiles=false
默認值:false
描述:是否合併Reduce的輸出文件,也就是在Map輸出階段作一次reduce操做,再輸出
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
這個參數表示執行前進行小文件合併,
前面三個參數肯定合併文件塊的大小,大於文件塊大小128m的,
按照128m來分隔,小於128m,大於100m的,按照100m來分隔,把那些小於100m的(包括小文件和分隔大文件剩下的),進行合併,最終生成了74個塊。
參考: