(六)Hive優化

小文件問題的影響
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輸入合并小文件的相關參數:

[java] view plain copy
//每個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輸出進行合并的相關參數:
[java] view plain copy
//設置map端輸出進行合并,默認為true
set hive.merge.mapfiles = true
//設置reduce端輸出進行合并,默認為false
set hive.merge.mapredfiles = true
//設置合并文件的大小
set hive.merge.size.per.task = 25610001000
//當輸出文件的平均大小小于該值時,啟動一個獨立的MapReduce任務進行文件merge。
set hive.merge.smallfiles.avgsize=16000000

————————————————

3.Write good SQL :

說道sql優化很慚愧,自己sql很爛,不多比比了,但是sql優化確實很關鍵。。。

4.存儲格式:

可以使用列裁剪,分區裁剪,orc,parquet等存儲格式。

Hive支持ORCfile,這是一種新的表格存儲格式,通過諸如謂詞下推,壓縮等技術來提高執行速度提升。
對于每個HIVE表使用ORCFile應該是一件容易的事情,并且對于獲得HIVE查詢的快速響應時間非常有益。
作為一個例子,考慮兩個大表A和B(作為文本文件存儲,其中一些列未在此處指定,即行試存儲的缺點)以及一個簡單的查詢,如:
SELECT A.customerID, A.name, A.age, A.address join
B.role, B.department, B.salary
ON A.customerID=B.customerID;
此查詢可能需要很長時間才能執行,因為表A和B都以TEXT形式存儲,進行全表掃描。
將這些表格轉換為ORCFile格式通常會顯著減少查詢時間:

  
  
ORC支持壓縮存儲(使用ZLIB或如上所示使用SNAPPY),但也支持未壓縮的存儲。
    CREATE TABLE A_ORC (
    customerID int, name string, age int, address string
    ) STORED AS ORC tblproperties (“orc.compress" = “SNAPPY”);

    INSERT INTO TABLE A_ORC SELECT * FROM A;


    CREATE TABLE B_ORC (
    customerID int, role string, salary float, department string
    ) STORED AS ORC tblproperties (“orc.compress" = “SNAPPY”);

    INSERT INTO TABLE B_ORC SELECT * FROM B;

    SELECT A_ORC.customerID, A_ORC.name,
    A_ORC.age, A_ORC.address join
    B_ORC.role, B_ORC.department, B_ORC.salary
    ON A_ORC.customerID=B_ORC.customerID;

5.壓縮格式:

壓縮格式 UNIX工具 算 法 文件擴展名 可分割
DEFLATE DEFLATE .deflate No
gzip gzip DEFLATE .gz No
LZ4 LZ4 .LZ4 NO
bzip bzip bzip .bz2 YES
LZO lzop LZO .lzo YES if indexed
Snappy Snappy .snappy NO
Size(MB).png
Time(s).png

大數據場景下存儲格式壓縮格式尤為關鍵,可以提升計算速度,減少存儲空間,降低網絡io,磁盤io,所以要選擇合適的壓縮格式和存儲格式,那么首先就了解這些東西,作者以前博客已經進行了詳細的說明,

可以看出壓縮比越高,壓縮時間越長,壓縮比:Snappy < LZ4 < LZO < GZIP < BZIP2

gzip:

優點:壓縮比在四種壓縮方式中較高;hadoop本身支持,在應用中處理gzip格式的文件就和直接處理文本一樣;有hadoop native庫;大部分linux系統都自帶gzip命令,使用方便。
缺點:不支持split。

lzo壓縮

優點:壓縮/解壓速度也比較快,合理的壓縮率;支持split,是hadoop中最流行的壓縮格式;支持hadoop native庫;需要在linux系統下自行安裝lzop命令,使用方便。
缺點:壓縮率比gzip要低;hadoop本身不支持,需要安裝;lzo雖然支持split,但需要對lzo文件建索引,否則hadoop也是會把lzo文件看成一個普通文件(為了支持split需要建索引,需要指定inputformat為lzo格式)。

snappy壓縮

優點:壓縮速度快;支持hadoop native庫。
缺點:不支持split;壓縮比低;hadoop本身不支持,需要安裝;linux系統下沒有對應的命令。

bzip2壓縮

優點:支持split;具有很高的壓縮率,比gzip壓縮率都高;hadoop本身支持,但不支持native;在linux系統下自帶bzip2命令,使用方便。
缺點:壓縮/解壓速度慢;不支持native。
————————————————

6.MAP JOIN

MapJoin.png

MapJoin簡單說就是在Map階段將小表讀入內存,順序掃描大表完成Join。

(1)通過MapReduce Local Task,將小表讀入內存,生成HashTableFiles上傳至Distributed Cache中,這里會對HashTableFiles進行壓縮。
(2)MapReduce Job在Map階段,每個Mapper從Distributed Cache讀取HashTableFiles到內存中,順序掃描大表,在Map階段直接進行Join,將數據傳遞給下一個MapReduce任務。
也就是在map端進行join避免了shuffle。
————————————————

7.引擎的選擇

Hive可以使用ApacheTez?執行引擎而不是古老的Map-Reduce引擎。 
我不會詳細討論在這里提到的使用Tez的許多好處; 相反,我想提出一個簡單的建議:
如果它沒有在您的環境中默認打開,請在您的Hive查詢的開頭將以下內容設置為'true'來使用Tez:
設置hive.execution.engine = tez;
通過上述設置,您執行的每個HIVE查詢都將利用Tez。
目前Hive On Spark還處于試驗階段,慎用。。

8.Use Vectorization

向量化查詢執行通過一次性批量執行1024行而不是每次單行執行,從而提高掃描,聚合,篩選器和連接等操作的性能。
在Hive 0.13中引入,此功能顯著提高了查詢執行時間,并可通過兩個參數設置輕松啟用:
設置hive.vectorized.execution.enabled = true;
設置hive.vectorized.execution.reduce.enabled = true;

9.cost based query optimization

Hive 自0.14.0開始,加入了一項”Cost based Optimizer”來對HQL執行計劃進行優化,這個功能通  
過”hive.cbo.enable”來開啟。在Hive 1.1.0之后,這個feature是默認開啟的,它可以自動優化HQL中多個JOIN的順序,并
選擇合適的JOIN算法.
Hive在提交最終執行前,優化每個查詢的執行邏輯和物理執行計劃。這些優化工作是交給底層來完成。
根據查詢成本執行進一步的優化,從而產生潛在的不同決策:如何排序連接,執行哪種類型的連接,并行度等等。
要使用基于成本的優化(也稱為CBO),請在查詢開始處設置以下參數:
設置hive.cbo.enable = true;

設置hive.compute.query.using.stats = true;

設置hive.stats.fetch.column.stats = true;

設置hive.stats.fetch.partition.stats = true;

10.模式選擇

本地模式

對于大多數情況,Hive可以通過本地模式在單臺機器上處理所有任務。
對于小數據,執行時間可以明顯被縮短。通過set hive.exec.mode.local.auto=true(默認為false)設置本地模式。
hive> set hive.exec.mode.local.auto;
hive.exec.mode.local.auto=false

并行模式

Hive會將一個查詢轉化成一個或者多個階段。這樣的階段可以是MapReduce階段、抽樣階段、合并階段、limit階段。
默認情況下,Hive一次只會執行一個階段,由于job包含多個階段,而這些階段并非完全互相依賴,
即:這些階段可以并行執行,可以縮短整個job的執行時間。設置參數:set hive.exec.parallel=true,或者通過配置文件來完成。
hive> set hive.exec.parallel;
hive.exec.parallel=false

嚴格模式

Hive提供一個嚴格模式,可以防止用戶執行那些可能產生意想不到的影響查詢,通過設置
Hive.mapred.modestrict來完成
set Hive.mapred.modestrict;
Hive.mapred.modestrict is undefined

11.JVM重用

Hadoop通常是使用派生JVM來執行map和reduce任務的。這時JVM的啟動過程可能會造成相當大的開銷,
尤其是執行的job包含偶成百上千的task任務的情況。JVM重用可以使得JVM示例在同一個job中時候使用N此。
通過參數mapred.job.reuse.jvm.num.tasks來設置。

12.推測執行

Hadoop推測執行可以觸發執行一些重復的任務,盡管因對重復的數據進行計算而導致消耗更多的計算資源,
不過這個功能的目標是通過加快獲取單個task的結果以偵測執行慢的TaskTracker加入到沒名單的方式來提高整體的任務執行效率。

Hadoop的推測執行功能由2個配置控制著,通過mapred-site.xml中配置

mapred.map.tasks.speculative.execution=true

mapred.reduce.tasks.speculative.execution=true
————————————————

Hive日常調優參數匯總

--壓縮配置:
-- map/reduce 輸出壓縮(一般采用序列化文件存儲)
set hive.exec.compress.output=true;
set mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec;
set mapred.output.compression.type=BLOCK;

--任務中間壓縮
set hive.exec.compress.intermediate=true;
set hive.intermediate.compression.codec=org.apache.hadoop.io.compress.SnappyCodec;
set hive.intermediate.compression.type=BLOCK;

--優化
set hive.exec.dynamic.partition.mode=nonstrict;--設置非嚴格模式
set hive.exec.dynamic.partition=true;--設置動態分區
set hive.exec.max.dynamic.partitions.pernode=1000;--設置動態分區每個節點最多可劃分為多少個分區
set hive.exec.max.dynamic.partitions=2000;--設置動態分區時的分區最大數量
set mapred.reduce.tasks = 20;--設置reduce的任務數量,可用于優化插入分區表時的執行效率
set hive.exec.reducers.max=100;--設置reduce最大數量
set spark.executor.cores=4;--設置每個executor用的core
set spark.executor.memory=8g;--設置每個executor的內存大小
set mapreduce.map.memory.mb=8192;--設置map任務的內存大小(container大小)
set mapreduce.reduce.memory.mb=8192;--設置reduce任務使用內存大小
set mapred.reduce.child.java.opts=-server -Xmx4000m -Djava.net.preferIPv4Stack=true;
--map端內存溢出可以參考下面兩個參數
set mapred.map.child.java.opts=-server -Xmx2048m -Djava.net.preferIPv4Stack=true;
set mapreduce.map.child.java.opts="-Xmx3072m"
set hive.execution.engine=mr;--設置執行hive引擎為mr
set hive.merge.mapredfiles= true;--合并小文件
set hive.merge.mapfiles = true;
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize = 256000000;
set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
set hive.optimize.cp = true;
set hive.exec.parallel=true;
set hive.exec.parallel.thread.number=8;
set mapreduce.job.running.map.limit=10;--限制map運行數量
set hive.mapjoin.smalltable.filesize=26214400;--默認是25M
set hive.exec.max.created.files = 200000;--增大hive文件創建數量
set yarn.app.mapreduce.am.resource.mb=4096;
set hive.tez.java.opts=-Xmx8192m -XX:MaxPermSize=256m;
SET hive.tez.container.size=10240;
set mapreduce.input.fileinputformat.split.maxsize=256000000;
set mapreduce.input.fileinputformat.split.minsize.per.node=256000000;
set mapreduce.input.fileinputformat.split.minsize.per.rack=256000000;
set hive.exec.reducers.bytes.per.reducer=5120000000;--設置每個reducer處理的數據
--總共有三種策略{"HYBRID", "BI", "ETL"}), 默認是"HYBRID","This is not a user level config.
-- BI strategy is used when the requirement is to spend less time in split generation as opposed to query execution (split generation does not read or cache file footers).
-- ETL strategy is used when spending little more time in split generation is acceptable (split generation reads and caches file footers).
-- HYBRID chooses between the above strategies based on heuristics."),
set hive.exec.orc.split.strategy=BI;(ORC split generation failed with exception: java.lang.OutOfMemoryError)
set hive.mapjoin.localtask.max.memory.usage=0.999;--本地任務可以使用內存的百分比 默認值:0.90
-- map join做group by操作時,可使用多大的內存來存儲數據。若數據太大則不會保存在內存里 默認值:0.55
set hive.mapjoin.followby.gby.localtask.max.memory.usage;

--本地mr設置
set hive.exec.mode.local.auto=true; --開啟本地mr
--設置local mr的最大輸入數據量,當輸入數據量小于這個值的時候會采用local mr的方式
set hive.exec.mode.local.auto.inputbytes.max=50000000;
--設置local mr的最大輸入文件個數,當輸入文件個數小于這個值的時候會采用local mr的方式
set hive.exec.mode.local.auto.tasks.max=10;
--當這三個參數同時成立時候,才會采用本地mr

set mapreduce.map.java.opts=-Xmx4096m -XX:-UseGCOverheadLimit -- GC overhead limit exceeded

set io.sort.mb=1024;
--采樣
set hive.limit.optimize.enable=true --- 開啟對數據源進行采樣的功能
set hive.limit.row.max.size --- 設置最小的采樣容量
set hive.limit.optimize.limit.file --- 設置最大的采樣樣本數

set mapred.max.split.size=134217728; --決定每個map處理的最大的文件大小,可以根據總文件大小以及這個參數的設置調整map的數量,動態調整,當map數量比較小且執行非常慢時,可以將此參數調小
set mapred.min.split.size.per.node=1024000000;--每個節點,動態調整,當map數量比較小且執行非常慢時,可以將此參數調小
set mapred.min.split.size.per.rack=1024000000;--每個機架
--mapred.max.split.size <= mapred.min.split.size.per.node <= mapred.min.split.size.per.rack
set hive.auto.convert.join=true; --hive自動識別小表,小表自動加載到內存,reduce端Common Join 轉化為map join,可解決數據傾斜問題,map端jpoin
--不產生shuffle
set hive.skewjoin.key=100000; --這個是join的鍵對應的記錄條數超過這個值則會進行分拆,值根據具體數據量設置
set hive.optimize.skewjoin = true;--如果是join 過程出現傾斜 應該設置為true,hive 在運行的時候沒有辦法判斷哪個key 會產生多大的傾斜,所以使用這個參數控制傾斜的閾值,如果超過這個值,新的值會發送給那些還沒有達到的reduce, 一般可以設置成(處理的總記錄數/reduce個數)的2-4倍都可以接受
set hive.groupby.mapaggr.checkinterval=100000;--這個是group的鍵對應的記錄條數超過這個值則會進行分拆,值根據具體數據量設置(map端聚合操作的記錄條數)
set hive.map.aggr.hash.min.reduction=0.5;--解釋:預先取100000條數據聚合,如果聚合后的條數小于100000*0.5,則不再聚合。
set hive.auto.convert.join.noconditionaltask=True;--將多個map join合并為一個,Hive在基于輸入文件大小的前提下將普通JOIN轉換成MapJoin,并是否將多個MJ合并成一個
set hive.auto.convert.join.noconditionaltask.size=100000000;--多個mapjoin轉換為1個時,所有小表的文件大小總和的最大值。
set hive.groupby.skewindata=false;--當選項設定為true,生成的查詢計劃會有兩個MRJob。第一個MRJob 中,
--Map的輸出結果集合會隨機分布到Reduce中,每個Reduce做部分聚合操作,并輸出結果,這樣處理的結果是相同的GroupBy Key
--有可能被分發到不同的Reduce中,從而達到負載均衡的目的;第二個MRJob再根據預處理的數據結果按照GroupBy Key分布到
--Reduce中(這個過程可以保證相同的GroupBy Key被分布到同一個Reduce中),最后完成最終的聚合操作。
set hive.optimize.index.filter=true;--自動使用索引,使用聚合索引優化group by操作,如果是orc表,可以使用orc的索引,加快讀取hive表的數據
set mapreduce.job.reduce.slowstart.completedmaps=0.8;--當Map Task完成的比例達到該值后才會為Reduce Task申請資源,默認是0.05,需要特別注意的是,
--在JobImpl中,如果處于Uber模式下,會將mapreduce.job.reduce.slowstart.completedmaps參數設置為1,這很好理解,因為不管Map Task,還是Reduce Task,均是串行執行的,所以當Map Task完成的比例達到多少值后才會為Reduce Task申請資源,這個值百分百應該是1

set hive.new.job.grouping.set.cardinality = 30;--grouping sets 數量較多時即cube維度過多,這條設置的意義在于告知解釋器,group by之前,每條數據復制量在30份以內。
-- 關閉hive推測執行
set hive.mapred.reduce.tasks.speculative.execution = false;
set mapreduce.map.speculative = false;
set mapreduce.reduce.speculative = false;

--hive on spark

set spark.executor.memory=4g;
set spark.executor.cores=2;
set spark.executor.instances=50;
set spark.serializer=org.apache.spark.serializer.KryoSerializer;
set spark.default.parallelism = 300;
set spark.locality.wait = 6;
set spark.locality.wait.process=6;
set spark.locality.wait.node=6;
set spark.locality.wait.rack=6;
set spark.shuffle.consolidateFiles=true;--map端文件合并
set spark.shuffle.memoryFraction=0.5;

set mapreduce.map.java.opts=-Xmx2000m -XX:-UseGCOverheadLimit

--map傾斜(數據量大且map分配數據量不合理)
set hive.exec.parallel=true;
set hive.exec.parallel.thread.number=2;
set hive.groupby.skewindata=true;
set mapred.max.split.size=256000000;
set mapred.min.split.size.per.node=256000000;
set mapred.min.split.size.per.rack=256000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
-- 當mapreduce.input.fileinputformat.split.maxsize > mapreduce.input.fileinputformat.split.minsize > dfs.blockSize的情況下,此時的splitSize 將由mapreduce.input.fileinputformat.split.minsize參數決定。
-- 當mapreduce.input.fileinputformat.split.maxsize > dfs.blockSize > mapreduce.input.fileinputformat.split.minsize的情況下,此時的splitSize 將由dfs.blockSize配置決定。(第二次優化符合此種情況)
-- 當dfs.blockSize > mapreduce.input.fileinputformat.split.maxsize > mapreduce.input.fileinputformat.split.minsize的情況下,此時的splitSize 將由mapreduce.input.fileinputformat.split.maxsize參數決定。
--maxSplitSize > minSplitSizeNode > minSplitSizeRack

set mapreduce.input.fileinputformat.split.maxsize=256000000;--map端文件切片大小
set mapreduce.input.fileinputformat.split.minsize.per.node=256000000;--同一節點的數據塊形成切片時,切片大小的最小值
set mapreduce.input.fileinputformat.split.minsize.per.rack=256000000;--同一機架的數據塊形成切片時,切片大小的最小值
----reduce端小文件合并(即MR任務結束后進行merge)
set hive.merge.mapfiles=true;
set hive.merge.mapredfiles=true;
set hive.merge.size.per.task=256000000;
set hive.merge.smallfiles.avgsize=256000000;

set dfs.namenode.handler.count=20;--設置該值的一般原則是將其設置為集群大小的自然對數乘以20,即20logN,N為集群大小。 https://blog.csdn.net/turk/article/details/79723963
set mapreduce.task.timeout=36000000;--MapReduce設置參數防止超時
--COST BASED QUERY OPTIMIZATION(CBO) cbo可以優化hive的每次查詢,使用CBO,需要開啟下面四個選項。
set hive.cbo.enable=true;--如果數據已經根據相同的key做好聚合,則去除多余的map/reduce作業
set hive.compute.query.using.stats=true;
set hive.stats.fetch.column.stats=true;
set hive.stats.fetch.partition.stats=true;

set mapreduce.reduce.shuffle.parallelcopies=10; shuffle開啟的fetcher線程數 apache默認5,choudera默認10
-- shuffle使用的內存比例。
mapreduce.reduce.shuffle.input.buffer.percent=0.6:--可以從0.2開始向上調。當只有20%的heap size分配給shuffle buffer的時候不容易出現OOM。
-- 單個shuffle任務能使用的內存限額,設置為0.15,即為 Shuffle內存 * 0.15。
-- 低于此值可以輸出到內存,否則輸出到磁盤。
mapreduce.reduce.shuffle.memory.limit.percent:
-- shuffle的數據量到Shuffle內存 ** 0.9的時候,啟動合并。
mapreduce.reduce.shuffle.merge.percent:設置為0.9。


set mapreduce.reduce.shuffle.memory.limit.percent=0.1;
set hive.map.aggr.hash.percentmemory = 0.25;--Hive Map 端聚合的哈稀存儲所占用虛擬機的內存比例。 當內存的Map大小,占到JVM配置的Map進程的25%的時候(默認是50%),就將這個數據flush到reducer去,以釋放內存Map的空間。
set hive.map.aggr.hash.force.flush.memory.threshold=0.9 --map端做聚合操作是hash表的最大可用內容,大于該值則會觸發flush
set hive.ignore.mapjoin.hint=false; --(默認值:true;是否忽略mapjoin hint 即HQL 語句中的 mapjoin 標記)
set hive.auto.convert.join.noconditionaltask=true; --(默認值:true;將普通的join轉化為普通的mapjoin時,是否將多個mapjoin轉化為一個mapjoin)
set hive.auto.convert.join.noconditionaltask.size=60000000;--(將多個mapjoin轉化為一個mapjoin時,其表的最大值)
set hive.stats.autogather=false;--即插入數據時會優化統計,如此在大的動態分區時load數據后會有一段很長時間的統計,且操作hive元數據表,例如每個分區的文件數,行數等等。耗時比較長時可能會timeout,需要將其設成false。

-- Hadoop任務可能引起OOM錯誤的原因有很多。一般情況下,首先檢查是否重設了hadoop參數:mapred.child.java.opts,一般設為-Xmx2000m,即使用2G的最大堆內存。
-- Hive中可能引起OOM的原因及相關的修復設定如下表所示:

-- 原因:map aggregation
-- map aggregation使用哈希表存儲group by/distinct key和他們的aggregation結果。
-- aggregate結果字段過多,或group by/distinct key的散度過大,可能導致內存占用過多。
-- 修復:
-- 減小hive.map.aggr.hash.percentmemory設定(默認為0.5,即使用50%的child堆內存)。

-- 原因:join
-- join需要cache所有相同join key的非驅動表的記錄
-- 修復:
-- 檢查是否把大表設定為驅動表(大表寫在join的最右邊)。
-- 如果已經設定正確的驅動表,減小hive.join.emit.interval設定(默認為1000,即每1000行的join結果集輸出一次)。

-- 原因:map join
-- map join需要cache全部小表的所有數據
-- 修復:
-- 檢查小表是否足夠小。如果小表超過1G,考慮不要使用map join。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。