Hive的壓縮儲存和簡單優化

MXC肖某某發表於2020-06-07

一、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只能使用1reducer來完成笛卡爾積

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=true2)設定為非嚴格模式(動態分割槽的模式,預設strict,表示必須指定至少一個分割槽為靜態分割槽,nonstrict模式表示允許所有的分割槽欄位都可以使用動態分割槽。)
set hive.exec.dynamic.partition.mode=nonstrict
(3)在所有執行MR的節點上,最大一共可以建立多少個動態分割槽。預設1000
set hive.exec.max.dynamic.partitions=10004)在每個執行MR的節點上,最大可以建立多少個動態分割槽。該引數需要根據實際的資料來設定。比如:源資料中包含了一年的資料,即day欄位有365個值,那麼該引數就需要設定成大於365,如果使用預設值100,則會報錯。
set hive.exec.max.dynamic.partitions.pernode=1005)整個MR Job中,最大可以建立多少個HDFS檔案。預設100000
set hive.exec.max.created.files=1000006)當有空分割槽生成時,是否丟擲異常。一般不需要設定。預設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=2560000002)每個任務最大的reduce數,預設為1009
hive.exec.reducers.max=10093)計算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的執行計劃

 

相關文章