數(shù)倉(cāng)面試高頻考點(diǎn)--解決hive小文件過(guò)多問(wèn)題
小文件產(chǎn)生原因
hive 中的小文件肯定是向 hive 表中導(dǎo)入數(shù)據(jù)時(shí)產(chǎn)生,所以先看下向 hive 中導(dǎo)入數(shù)據(jù)的幾種方式
直接向表中插入數(shù)據(jù)
insert?into?table?A?values?(1,'zhangsan',88),(2,'lisi',61);這種方式每次插入時(shí)都會(huì)產(chǎn)生一個(gè)文件,多次插入少量數(shù)據(jù)就會(huì)出現(xiàn)多個(gè)小文件,但是這種方式生產(chǎn)環(huán)境很少使用,可以說(shuō)基本沒(méi)有使用的
通過(guò)load方式加載數(shù)據(jù)
load data local inpath '/export/score.csv' overwrite into table A -- 導(dǎo)入文件load data local inpath '/export/score' overwrite into table A -- 導(dǎo)入文件夾
使用 load 方式可以導(dǎo)入文件或文件夾,當(dāng)導(dǎo)入一個(gè)文件時(shí),hive表就有一個(gè)文件,當(dāng)導(dǎo)入文件夾時(shí),hive表的文件數(shù)量為文件夾下所有文件的數(shù)量
通過(guò)查詢方式加載數(shù)據(jù)
insert?overwrite?table?A??select?s_id,c_name,s_score?from?B;
這種方式是生產(chǎn)環(huán)境中常用的,也是最容易產(chǎn)生小文件的方式
insert 導(dǎo)入數(shù)據(jù)時(shí)會(huì)啟動(dòng) MR 任務(wù),MR中 reduce 有多少個(gè)就輸出多少個(gè)文件
所以, 文件數(shù)量=ReduceTask數(shù)量*分區(qū)數(shù)
也有很多簡(jiǎn)單任務(wù)沒(méi)有reduce,只有map階段,則
文件數(shù)量=MapTask數(shù)量*分區(qū)數(shù)
每執(zhí)行一次 insert 時(shí)hive中至少產(chǎn)生一個(gè)文件,因?yàn)?insert 導(dǎo)入時(shí)至少會(huì)有一個(gè)MapTask。
像有的業(yè)務(wù)需要每10分鐘就要把數(shù)據(jù)同步到 hive 中,這樣產(chǎn)生的文件就會(huì)很多。
小文件過(guò)多產(chǎn)生的影響
首先對(duì)底層存儲(chǔ)HDFS來(lái)說(shuō),HDFS本身就不適合存儲(chǔ)大量小文件,小文件過(guò)多會(huì)導(dǎo)致namenode元數(shù)據(jù)特別大, 占用太多內(nèi)存,嚴(yán)重影響HDFS的性能
對(duì) hive 來(lái)說(shuō),在進(jìn)行查詢時(shí),每個(gè)小文件都會(huì)當(dāng)成一個(gè)塊,啟動(dòng)一個(gè)Map任務(wù)來(lái)完成,而一個(gè)Map任務(wù)啟動(dòng)和初始化的時(shí)間遠(yuǎn)遠(yuǎn)大于邏輯處理的時(shí)間,就會(huì)造成很大的資源浪費(fèi)。而且,同時(shí)可執(zhí)行的Map數(shù)量是受限的。
怎么解決小文件過(guò)多
1. 使用 hive 自帶的 concatenate 命令,自動(dòng)合并小文件
使用方法:
#對(duì)于非分區(qū)表alter table A concatenate;#對(duì)于分區(qū)表alter table B partition(day=20201224) concatenate;
舉例:
hive (default)> insert into table A values (1,'aa',67),(2,'bb',87);hive (default)> insert into table A values (3,'cc',67),(4,'dd',87);hive (default)> insert into table A values (5,'ee',67),(6,'ff',87);hive (default)> dfs -ls /user/hive/warehouse/A;Found 3 items-rwxr-xr-x 3 root supergroup 378 2020-12-24 14:46 /user/hive/warehouse/A/000000_0-rwxr-xr-x 3 root supergroup 378 2020-12-24 14:47 /user/hive/warehouse/A/000000_0_copy_1-rwxr-xr-x 3 root supergroup 378 2020-12-24 14:48 /user/hive/warehouse/A/000000_0_copy_2hive (default)> alter table A concatenate;hive (default)> dfs -ls /user/hive/warehouse/A;Found 1 items-rwxr-xr-x 3 root supergroup 778 2020-12-24 14:59 /user/hive/warehouse/A/000000_0
注意:?
1、concatenate 命令只支持 RCFILE 和 ORC 文件類型。?
2、使用concatenate命令合并小文件時(shí)不能指定合并后的文件數(shù)量,但可以多次執(zhí)行該命令。?
3、當(dāng)多次使用concatenate后文件數(shù)量不在變化,這個(gè)跟參數(shù) mapreduce.input.fileinputformat.split.minsize=256mb 的設(shè)置有關(guān),可設(shè)定每個(gè)文件的最小size。
2. 調(diào)整參數(shù)減少M(fèi)ap數(shù)量
設(shè)置map輸入合并小文件的相關(guān)參數(shù):
#執(zhí)行Map前進(jìn)行小文件合并#CombineHiveInputFormat底層是 Hadoop的 CombineFileInputFormat 方法#此方法是在mapper中將多個(gè)文件合成一個(gè)split作為輸入set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; -- 默認(rèn)#每個(gè)Map最大輸入大小(這個(gè)值決定了合并后文件的數(shù)量)set mapred.max.split.size=256000000; -- 256M#一個(gè)節(jié)點(diǎn)上split的至少的大小(這個(gè)值決定了多個(gè)DataNode上的文件是否需要合并)set mapred.min.split.size.per.node=100000000; -- 100M#一個(gè)交換機(jī)下split的至少的大小(這個(gè)值決定了多個(gè)交換機(jī)上的文件是否需要合并)set mapred.min.split.size.per.rack=100000000; -- 100M
設(shè)置map輸出和reduce輸出進(jìn)行合并的相關(guān)參數(shù):
#設(shè)置map端輸出進(jìn)行合并,默認(rèn)為trueset hive.merge.mapfiles = true;#設(shè)置reduce端輸出進(jìn)行合并,默認(rèn)為falseset hive.merge.mapredfiles = true;#設(shè)置合并文件的大小set hive.merge.size.per.task = 256*1000*1000; -- 256M#當(dāng)輸出文件的平均大小小于該值時(shí),啟動(dòng)一個(gè)獨(dú)立的MapReduce任務(wù)進(jìn)行文件mergeset hive.merge.smallfiles.avgsize=16000000; -- 16M
啟用壓縮
# hive的查詢結(jié)果輸出是否進(jìn)行壓縮set hive.exec.compress.output=true;# MapReduce Job的結(jié)果輸出是否使用壓縮set mapreduce.output.fileoutputformat.compress=true;
3. 減少Reduce的數(shù)量
#reduce 的個(gè)數(shù)決定了輸出的文件的個(gè)數(shù),所以可以調(diào)整reduce的個(gè)數(shù)控制hive表的文件數(shù)量,#hive中的分區(qū)函數(shù) distribute by 正好是控制MR中partition分區(qū)的,#然后通過(guò)設(shè)置reduce的數(shù)量,結(jié)合分區(qū)函數(shù)讓數(shù)據(jù)均衡的進(jìn)入每個(gè)reduce即可。#設(shè)置reduce的數(shù)量有兩種方式,第一種是直接設(shè)置reduce個(gè)數(shù)set mapreduce.job.reduces=10;#第二種是設(shè)置每個(gè)reduce的大小,Hive會(huì)根據(jù)數(shù)據(jù)總大小猜測(cè)確定一個(gè)reduce個(gè)數(shù)set hive.exec.reducers.bytes.per.reducer=5120000000; -- 默認(rèn)是1G,設(shè)置為5G#執(zhí)行以下語(yǔ)句,將數(shù)據(jù)均衡的分配到reduce中set mapreduce.job.reduces=10;insert overwrite table A partition(dt)select * from Bdistribute by rand();解釋:如設(shè)置reduce數(shù)量為10,則使用 rand(), 隨機(jī)生成一個(gè)數(shù) x % 10 ,這樣數(shù)據(jù)就會(huì)隨機(jī)進(jìn)入 reduce 中,防止出現(xiàn)有的文件過(guò)大或過(guò)小
4. 使用hadoop的archive將小文件歸檔
Hadoop Archive簡(jiǎn)稱HAR,是一個(gè)高效地將小文件放入HDFS塊中的文件存檔工具,它能夠?qū)⒍鄠€(gè)小文件打包成一個(gè)HAR文件,這樣在減少namenode內(nèi)存使用的同時(shí),仍然允許對(duì)文件進(jìn)行透明的訪問(wèn)
#用來(lái)控制歸檔是否可用set hive.archive.enabled=true;#通知Hive在創(chuàng)建歸檔時(shí)是否可以設(shè)置父目錄set hive.archive.har.parentdir.settable=true;#控制需要?dú)w檔文件的大小set har.partfile.size=1099511627776;#使用以下命令進(jìn)行歸檔ALTER TABLE A ARCHIVE PARTITION(dt='2020-12-24', hr='12');#對(duì)已歸檔的分區(qū)恢復(fù)為原文件ALTER TABLE A UNARCHIVE PARTITION(dt='2020-12-24', hr='12');
注意: ?
歸檔的分區(qū)可以查看不能 insert overwrite,必須先 unarchive
最后
如果是新集群,沒(méi)有歷史遺留問(wèn)題的話,建議hive使用 orc 文件格式,以及啟用 lzo 壓縮。
這樣小文件過(guò)多可以使用hive自帶命令 concatenate 快速合并。
