一、Hive的壓縮和儲存
1,MapReduce支援的壓縮編碼
壓縮格式 |
工具 |
演算法 |
副檔名 |
是否可切分 |
對應的編碼/解碼器 |
DEFLATE |
無 |
DEFLATE |
.deflate |
否 |
org.apache.hadoop.io.compress.DefaultCodec |
Gzip |
gzip |
DEFLATE |
.gz |
否 |
org.apache.hadoop.io.compress.GzipCodec |
bzip2 |
bzip2 |
bzip2 |
.bz2 |
是 |
org.apache.hadoop.io.compress.BZip2Codec |
LZO |
lzop |
LZO |
.lzo |
是 |
com.hadoop.compression.lzo.LzopCodec |
Snappy |
無 |
Snappy |
.snappy |
否 |
org.apache.hadoop.io.compress.SnappyCodec |
2,檔案壓縮格式:
TEXTFILE和SEQUENCEFILE的儲存格式都是基於行式儲存的;
ORC和PARQUET是基於列式儲存的。
a>TextFile格式:
預設格式,資料不做壓縮,磁碟開銷大,資料解析開銷大。可結合Gzip、Bzip2使用,但使用Gzip這種方式,hive不會對資料進行切分,從而無法對資料進行並行操作。
b>Orc格式:
Hive 0.11版裡引入的新的儲存格式,資料按行分塊 每塊按照列儲存 ,壓縮快 快速列存取,效率比rcfile高,是rcfile的改良版本,相比RC能夠更好的壓縮,能夠更快的查詢,但還是不支援模式演進。
c>parquent格式:
Parquet檔案是以二進位制方式儲存的,所以是不可以直接讀取的,檔案中包括該檔案的資料和後設資料,因此Parquet格式檔案是自解析的。
在實際的專案開發當中,hive表的資料儲存格式一般選擇:orc或parquet。壓縮方式一般選擇snappy,lzo。
二、Hive的企業級調優:
1,Fetch抓取:
預設開啟。Fetch抓取是指,Hive中對某些情況的查詢可以不必使用MapReduce計算。(hive-default.xml.template檔案中hive.fetch.task.conversion預設是more)例如:SELECT * FROM person;在這種情況下,Hive可以簡單地讀取person對應的儲存目錄下的檔案,然後輸出查詢結果到控制檯。
2,本地模式:
大多數的Hadoop Job是需要Hadoop提供的完整的可擴充套件性來處理大資料集的。不過,有時Hive的輸入資料量是非常小的。在這種情況下,為查詢觸發執行任務消耗的時間可能會比實際job的執行時間要多的多。對於大多數這種情況,Hive可以通過本地模式在單臺機器上處理所有的任務。對於小資料集,執行時間可以明顯被縮短。
set hive.exec.mode.local.auto=true; //開啟本地mr //設定local mr的最大輸入資料量,當輸入資料量小於這個值時採用local mr的方式,預設為134217728,即128M set hive.exec.mode.local.auto.inputbytes.max=50000000; //設定local mr的最大輸入檔案個數,當輸入檔案個數小於這個值時採用local mr的方式,預設為4 set hive.exec.mode.local.auto.input.files.max=10;
3,表的優化:
a>大小表的join:
新版的hive已經對小表JOIN大表和大表JOIN小表進行了優化。小表放在左邊和右邊已經沒有明顯區別。
b>大表join大表:
空key過濾:空key對應的資料無意義
select n.* from (select * from nullidtable where id is not null ) n left join ori o on n.id = o.id;
空key轉換:空key對應的資料還是有意義,需要保留
#為空key賦予隨機值,在進入reduce的時候防止空key太多而造成資料傾斜
select n.* from nullidtable n full join ori o on case when n.id is null then concat('hive', rand()) else n.id end = o.id;
c>MapJoin(小表join大表)
#預設開啟 set hive.auto.convert.join = true; #大表小表的閾值設定(預設25M以下認為是小表)可以調整 set hive.mapjoin.smalltable.filesize=25000000;
d>group by:
預設情況下,Map階段同一個key資料分發到同一個reduce,當一個key的資料過大時就會出現資料傾斜。
#是否在Map端進行聚合,預設為True set hive.map.aggr = true
#在Map端進行聚合操作的條目數目 set hive.groupby.mapaggr.checkinterval = 100000
#有資料傾斜的時候進行負載均衡(預設是false) set hive.groupby.skewindata = true
當設定為負載均衡之後,生成的計劃會有兩個MR的job。第一個MRjob,Map的輸出結果可能會隨機分佈到Reduce中,每個Reduce做部分聚合操作,並輸出結果,這樣處理的結果是相同的Group By Key有可能被分發到不同的Reduce中,從而達到負載均衡的目的;第二個MRjob再根據預處理的資料結果按照Group By Key分佈到Reduce中(這個過程可以保證相同的Group By Key被分佈到同一個Reduce中),最後完成最終的聚合操作。
e>Count(distinct)去重統計
資料量小的時候無所謂,資料量大的情況下,由於COUNT DISTINCT的全聚合操作,即使設定了reduce task個數,set mapred.reduce.tasks=100;hive也只會啟動一個reducer,這就造成一個Reduce處理的資料量太大,導致整個Job很難完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替換:
#設定reduce個數為5 set mapreduce.job.reduces = 5; #採用distinct去重 select count(distinct id) from bigtable; #採用group by去重 select count(id) from (select id from bigtable group by id) a;
雖然會多用一個Job來完成,但在資料量大的情況下,這個絕對是值得的。
f>笛卡爾積
儘量避免笛卡爾積,join的時候不加on條件,或者無效的on條件,Hive只能使用1個reducer來完成笛卡爾積。
g>行列過濾
列處理:在select中,只拿需要的列,如果有,儘量使用分割槽過濾,少用select *。
行處理:在分割槽剪裁中,當使用外關聯時,如果將副表的過濾條件寫在where後面,那麼就會先全表關聯,之後再過濾。
#先關聯再過濾 select o.id from bigtable b join ori o on o.id = b.id #先過濾再關聯 select b.id from bigtable b join (select id from ori where id <= 10 ) o on b.id = o.id;
h>動態分割槽
(1)開啟動態分割槽功能(預設true,開啟) set hive.exec.dynamic.partition=true (2)設定為非嚴格模式(動態分割槽的模式,預設strict,表示必須指定至少一個分割槽為靜態分割槽,nonstrict模式表示允許所有的分割槽欄位都可以使用動態分割槽。) set hive.exec.dynamic.partition.mode=nonstrict (3)在所有執行MR的節點上,最大一共可以建立多少個動態分割槽。預設1000 set hive.exec.max.dynamic.partitions=1000 (4)在每個執行MR的節點上,最大可以建立多少個動態分割槽。該引數需要根據實際的資料來設定。比如:源資料中包含了一年的資料,即day欄位有365個值,那麼該引數就需要設定成大於365,如果使用預設值100,則會報錯。 set hive.exec.max.dynamic.partitions.pernode=100 (5)整個MR Job中,最大可以建立多少個HDFS檔案。預設100000 set hive.exec.max.created.files=100000 (6)當有空分割槽生成時,是否丟擲異常。一般不需要設定。預設false set hive.error.on.empty.partition=false
i>分割槽
分割槽表實際上就是對應一個HDFS檔案系統上的獨立的資料夾,該資料夾下是該分割槽所有的資料檔案。Hive中的分割槽就是分目錄,把一個大的資料集根據業務需要分割成小的資料集。在查詢時通過WHERE子句中的表示式選擇查詢所需要的指定的分割槽,這樣的查詢效率會提高很多。
j>分桶
分割槽提供一個隔離資料和優化查詢的便利方式。不過,並非所有的資料集都可形成合理的分割槽。對於一張表或者分割槽,Hive 可以進一步組織成桶,也就是更為細粒度的資料範圍劃分。分割槽針對的是資料的儲存路徑;分桶針對的是資料檔案。
#建立分桶表
create table stu_buck(id int, name string)
clustered by(id)
into 4 buckets
row format delimited fields terminated by '\t';
#建立中間表
create table stu(id int, name string) row format delimited fields terminated by '\t';
#匯入資料到中間表
load data local inpath '/opt/module/datas/student.txt' into table stu;
#開啟分桶
set hive.enforce.bucketing=true;
#設定reduce數量為-1
set mapreduce.job.reduces=-1;
#向分桶表中匯入資料
insert into table stu_buck select id, name from stu;
4,合理設定Map的數量和Reduce的數量
a>複雜檔案增加map數量
增加map的方法為:根據computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M公式,調整maxSize最大值。
讓maxSize最大值低於blocksize就可以增加map的個數。
set mapreduce.input.fileinputformat.split.maxsize=100;
b>小檔案進行合併
#設定為CombineHiveInputFormat合併小檔案 set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; #在map-only任務結束時合併小檔案,預設true set hive.merge.mapfiles = true; #在map-reduce任務結束時合併小檔案,預設false set hive.merge.mapredfiles = true; #合併檔案的大小,預設256M set hive.merge.size.per.task = 268435456; #當輸出檔案的平均大小小於該值時,啟動一個獨立的map-reduce任務進行檔案merge set hive.merge.smallfiles.avgsize = 16777216;
c>合理設定reduce的個數
1.調整reduce個數方法一
(1)每個Reduce處理的資料量預設是256MB hive.exec.reducers.bytes.per.reducer=256000000 (2)每個任務最大的reduce數,預設為1009 hive.exec.reducers.max=1009 (3)計算reducer數的公式 N=min(引數2,總輸入資料量/引數1)
2.調整reduce個數方法二
在hadoop的mapred-default.xml檔案中修改 設定每個job的Reduce個數 set mapreduce.job.reduces = 15;
3.reduce個數並不是越多越好
1)過多的啟動和初始化reduce也會消耗時間和資源; 2)另外,有多少個reduce,就會有多少個輸出檔案,如果生成了很多個小檔案,那麼如果這些小檔案作為下一個任務的輸入,則也會出現小檔案過多的問題; 在設定reduce個數的時候也需要考慮這兩個原則:處理大資料量利用合適的reduce數;使單個reduce任務處理資料量大小要合適;
5,並行執行
Hive會將一個查詢轉化成一個或者多個階段。這樣的階段可以是MapReduce階段、抽樣階段、合併階段、limit階段。或者Hive執行過程中可能需要的其他階段。預設情況下,Hive一次只會執行一個階段。不過,某個特定的job可能包含眾多的階段,而這些階段可能並非完全互相依賴的,也就是說有些階段是可以並行執行的,這樣可能使得整個job的執行時間縮短。不過,如果有更多的階段可以並行執行,那麼job可能就越快完成。
set hive.exec.parallel=true; //開啟任務並行執行 set hive.exec.parallel.thread.number=16; //同一個sql允許最大並行度,預設為8。
在共享叢集中,需要注意下,如果job中並行階段增多,那麼叢集利用率就會增加。當然,得是在系統資源比較空閒的時候才有優勢,否則,沒資源,並行也起不來。
6,嚴格模式
通過設定屬性hive.mapred.mode值為預設是非嚴格模式nonstrict 。開啟嚴格模式需要修改hive.mapred.mode值為strict,開啟嚴格模式可以禁止3種型別的查詢。
1)對於分割槽表,除非where語句中含有分割槽欄位過濾條件來限制範圍,否則不允許執行。(就是使用者不允許掃描所有分割槽)
2)對於使用了order by語句的查詢,要求必須使用limit語句。 因為order by為了執行排序過程會將所有的結果資料分發到同一個Reducer中進行處理,強制要求使用者增加這個LIMIT語句可以防止Reducer額外執行很長一段時間。
3)限制笛卡爾積的查詢。
7,JVM重用
JVM重用是Hadoop調優引數的內容,其對Hive的效能具有非常大的影響,特別是對於很難避免小檔案的場景或task特別多的場景,這類場景大多數執行時間都很短。
在Hadoop的mapred-site.xml檔案中進行配置
<property> <name>mapreduce.job.jvm.numtasks</name> <value>10</value> <description>How many tasks to run per jvm. If set to -1, there is no limit</description> </property>
8,推測執行
Hadoop的mapred-site.xml檔案中進行配置,預設是true
<property> <name>mapreduce.map.speculative</name> <value>true</value> <description>If true, then multiple instances of some map tasks may be executed in parallel.</description> </property> <property> <name>mapreduce.reduce.speculative</name> <value>true</value> <description>If true, then multiple instances of some reduce tasks may be executed in parallel.</description> </property>
不過hive本身也提供了配置項來控制reduce-side的推測執行:預設是true
<property> <name>hive.mapred.reduce.tasks.speculative.execution</name> <value>true</value> <description>Whether speculative execution for reducers should be turned on. </description> </property>
不建議開啟的情況:(1)任務間存在嚴重的負載傾斜;(2)特殊任務,比如任務向資料庫中寫資料。
9,壓縮
見第一章
10,explain執行計劃
利用explain檢視sql的執行計劃