小文件問題的影響
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 |
大數據場景下存儲格式壓縮格式尤為關鍵,可以提升計算速度,減少存儲空間,降低網絡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簡單說就是在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。