Hadoop 小文件處理


1. 小文件的產生原因

定義: 當一個文件的大小小於 HDFS 的塊大小(默認128MB)就認定為小文件,否則就是大文件

  1. 批處理,離線計算, 會有小文件的產生;

  2. 數據處理時,把數據源搬遷到 HDFS,如果數據源本身就是有很多小文件;

  3. MapReduce作業 和 Spark作業時,沒有設置好 Reduce Task個數,或者spark最后一層Task的數量。

2. 小文件的危害

  1. HDFS不適合大量小文件的存儲,因namenode將文件系統的元數據存放在內存中,因此存儲的文件數目受限於 namenode的內存大小。HDFS中每個文件、目錄、數據塊 占用150Bytes。如果存放的文件數目過多的話會占用很大的內存甚至撐爆內存;

  2. HDFS適用於高吞吐量,而不適合低時間延遲的訪問。如果同時存入大量的小文件會花費很長的時間;

  3. 流式讀取的方式,不適合多用戶寫入,以及任意位置寫入。如果訪問小文件,則必須從一個DataNode跳轉到另外一個DataNode,這樣大大降低了讀取性能;

  4. 小文件過多,盡管hadoop集群配置了HA ,一旦原先NameNode掛掉, 備用的NameNode也會因小文件多,導致Block多,NameNode啟動是把FsImage磁盤數據和Edits操作日志加載到內存的過程,Block越多,讀取耗時也越長,啟動就會很慢;

3. 解決方案

處理指導原則:

  • 在寫入HDFS前進行處理 (優先考慮,此處只考慮了mr產生的,暫不考慮基於tez,spark等其他寫入hdfs的途徑)

      1. 輸入合並,在Map前合並小文件;
      1. 輸出合並,在Reduce寫入磁盤前合並小文件;
  • 在寫入hdfs后進行處理

      1. HDFS中的小文件合並

具體操作方法:
對於小文件合並,一般的做法是編寫腳本、程序完成合並。

3.1 通過參數調節,設置map/reduce的數量

#設置map輸入合並小文件的相關參數:

//每個Map最大輸入大小(這個值決定了合並后文件的數量,調大可以減小Mapper數),默認128M
set mapred.max.split.size=1024*1024*512;  

//一個節點上split的至少的大小(小於這個值會進行合並,這個值決定了多個DataNode上的文件是否需要合並)
set mapred.min.split.size.per.node=1024*1024*512;

//一個交換機下split的至少的大小(這個值決定了多個交換機上的文件是否需要合並)  
set mapred.min.split.size.per.rack=100000000;

//設置hive輸入端進行小文件合並
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

# 減少reduce的數量
-- 除非知道數據量的分布,一般不直接設置
set mapred.reduce.tasks=10;

-- 每個reduce處理的數據量,實際上可能每個reduce 處理的數據量會超過它,默認1G 增加該參數,可以減小reduce數量
set hive.exec.reducers.bytes.per.reducer=1073741824 


# 設置map輸出和reduce輸出進行合並的相關參數:
//設置map端輸出進行合並,默認為true
set hive.merge.mapfiles = true

//設置reduce端輸出進行合並,默認為false
set hive.merge.mapredfiles = true

//設置合並文件的大小
set hive.merge.size.per.task = 256*1000*1000

//當輸出文件的平均大小小於該值時,啟動一個獨立的MapReduce任務進行文件merge。
set hive.merge.smallfiles.avgsize=16000000

//ps:理論上設置了輸出端小文件合並,會減少小文件的數量,可惜實際中即使設置了這些參數,對tdw分區表並不起作用

// hadoop v2.x 后一些參數名發生了變化
mapred.min.split.size => mapreduce.input.fileinputformat.split.minsize。
mapred.max.split.size => mapreduce.input.fileinputformat.split.maxsize。
mapred.min.split.size.per.rack => mapreduce.input.fileinputformat.split.minsize.per.rack。
mapred.min.split.size.per.node => mapreduce.input.fileinputformat.split.minsize.per.node。
dfs.block.size => dfs.blocksize
mapred.map.tasks => mapreduce.job.maps

3.2 hadoop 自帶的小文件處理方法

1) Hadoop Archive 小文件歸檔

// 創建歸檔文件 把 input目錄下所有文件 歸檔到 output/input.har, 原先input下的文件依然存在,需要手動刪除才能釋放原先在namenode中元數據占用的內存
hadoop archive -archiveName input.har -p /user/kuncai/input /user/kuncai/output

// 查看歸檔文件
hadoop fs -ls har:///user/kuncai/output3/input.har

// 解壓歸檔文件
hadoop fs -cp har:///user/kuncai/output3/input.har/* /user/kuncai/

ps: 文件是許多記錄組成的,那么可以通過調用 HDFS 的 sync() 方法(和 append 方法結合使用),每隔一定時間生成一個大文件。

另外:hive中的數據,也可以通過sort by 和 distrubute by 重建表,建表時減少reduce的數量,從而減少小文件;

2) SequenceFile 合並小文件;

原理: 使用文件名作為 key,文件內容作為 value ,把多個文件數據格式轉換后統一輸出;

通過自定義MR中的 Mapper,Driver,Reducer方法;

具體代碼實現可參考此鏈接: https://examples.javacodegeeks.com/enterprise-java/apache-hadoop/hadoop-sequence-file-example/

3) CombineFileInputFormat<K,V> 小文件合並

核心思想:

​ 根據一定的規則,將 HDFS 上多個小文件合並到一個 InputSplit 中,然后會啟用一個 Map 來處理這里面的文件,以此減少MR整體作業的運行時間

具體代碼實現可以參考此鏈接: https://blog.csdn.net/u011007180/article/details/52333387

4) HBase

如果你產生很多小文件,根據訪問模式的不同,應該進行不同類型的存儲。HBase 將數據存儲在 Map Files(帶索引的 SequenceFile)中,如果你需要隨機訪問來執行 MapReduce 流式分析,這是一個不錯的選擇。如果延遲是一個問題,那么還有很多其他選擇 - 參見Richard Jones對鍵值存儲的調查。

Other參考鏈接:

Hadoop 大量小文件問題的優化 https://cloud.tencent.com/developer/article/1482598

Hive-小文件優化實戰 https://www.jianshu.com/p/8f0ce9eb0d0b


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM