大數據面試 | 06 我看了10篇文章,總結出的Hadoop小文件最全學習攻略

1. 什么是小文件

小文件的定義和hadoop中定義的block大小有關,這里把所有遠遠小于hadoop block大小的文件稱為小文件。hadoop block塊大小通常設置為128MB、256MB,趨向于越來越大。這里假定如果文件大小是小于block size的75%,則定義為小文件。

但小文件不僅是指文件比較小,如果Hadoop集群中的大量文件略大于block size,同樣也會存在小文件問題。比如,假設block size是128MB,但加載到Hadoop的所有文件都是136MB,就會存在大量8MB的block。處理這種“小塊”問題你可以調大block size來解決,但解決小文件問題卻要復雜的多。

2. 小文件產生的原因

一個Hadoop集群中存在小文件問題是很正常的,可能的原因如下:

  1. 現(xiàn)在我們越來越多的將Hadoop用于(準)實時計算,在做數據抽取時處理的頻率可能是每小時,每天,每周等,每次可能就只生成一個不到10MB的文件。即公司對實時數據的渴望,獲取數據的時間間隔的縮短,導致采集進入hadoop的數據文件偏小。

2.數據源有大量小文件,未做處理直接拷貝到Hadoop集群。

3.MapReduce作業(yè)的配置未設置合理的reducer或者未做限制,每個reduce都會生成一個獨立的文件。另外如果數據傾斜,導致大量的數據都shuffle到一個reduce,然后其他的reduce都會處理較小的數據量并輸出小文件。

3. Hadoop的小文件對NameNode內存管理造成影響。

Hadoop中的每個目錄、文件和block都會以對象的形式保存在NameNode的內存中。

根據經驗每個對象在內存中大概占用150個字節(jié)。如果HDFS中保存2000萬個文件,每個文件都在同一個文件夾中,而且每個文件都只有一個block,則NameNode需要6GB內存。隨著數據量的增長集群的擴容,最終會達到單臺NameNode可以處理的文件(block)數量的上限?;谕瑯拥募僭O,如果HDFS中保存的數據文件增長到10億個,則NameNode需要300GB內存。這會對NameNode有什么影響:

  1. NameNode重啟時,它都需要從本地磁盤讀取每個文件的元數據,意味著你要讀取300GB數據到內存中,不可避免導致NameNode啟動時間較長。

  2. 一般來說,NameNode會不斷跟蹤并檢查每個數據塊的存儲位置。這是通過DataNode的定時心跳上報其數據塊來實現(xiàn)的。數據節(jié)點需要上報的block越多,則也會消耗越多的網絡帶寬/時延。

  3. NameNode本身使用300G內存,相當于JVM你需要配置300GB的heap,對于JVM來說本來就存在穩(wěn)定性的風險,比如GC時間較長。

所以如果可以減少集群上的小文件數,則可以減少NameNode的內存占用,啟動時間以及網絡影響。

4. Hadoop的小文件問題主要是會對MapReduce性能造成影響

如果集群中有大量小文件,會降低MapReduce的處理性能,無論是Hive,Pig還是Java MapReduce,當然其實其他計算引擎比如Spark,Impala也會受到影響。


  1. 第一個原因是大量小文件意味著大量的隨機磁盤IO。磁盤IO通常是MapReduce性能的最大瓶頸之一,在HDFS中對于相同數量的數據,一次大的順序讀取往往優(yōu)于幾次隨機讀取的性能。

  2. 當MapReduce任務啟動時,每個數據block會被分配為一個map任務。HDFS中的每個文件至少是一個block。如果你有10000個文件,而且每個文件10MB,那么這個MapReduce作業(yè)會被分配為10000個map任務。一般來說每個Hadoop的map任務會運行在自己的JVM中,所以會帶來10000個JVM的啟動和關閉的資源開銷。
    集群的資源是有限的,為了方便計算,假設我們在YARN的配置中為每個NodeManager配置20個vcore,那么為了同時運行10000個mapper,你需要500臺節(jié)點。大多數Hadoop集群都小于這個規(guī)模,所以一般情況下大量map任務可能只能排隊等待ResourceManager來分配資源。如果你只有10臺機器,那么總共只有200個vcore,這個排隊的隊列會較大,相應的處理時間也會變的較長。
    如果10000個10MB文件換成800個128MB的文件,那么你就只需要800個map任務。相當于減少了一個數量級的JVM維護時間,同時也優(yōu)化了磁盤IO。

5.解決NameNode的內存問題

上面的內容提到過每個block的元數據都需要加載到NameNode的內存中,這導致一個Hadoop集群在NameNode中存儲的對象是有上限的,并且對象太多會帶來啟動時間較長以及網絡延遲的問題。常見的有兩種解決方案,減少集群的NameNode中的對象數量,或者以某種方式讓NameNode使用更多的“內存”但不會導致較長的啟動時間,這就是Hadoop Archive(HAR)文件和NameNode聯(lián)邦。

1. Hadoop Archive Files

Hadoop archive files通過將許多小文件打包到更大的HAR文件中來緩解NameNode內存問題,類似于Linux上的TAR文件。這樣可以讓NameNode只處理單個HAR文件,而不是數十個或數百個小文件??梢允褂胔ar://前綴而不是hdfs://來訪問HAR文件中的文件。HAR文件是基于HDFS中已有的文件創(chuàng)建的。因此,HAR文件不僅可以合并從數據源抽取到HDFS中的數據,也可以合并通過正常的MapReduce處理創(chuàng)建的數據。HAR文件可以獨立的用于解決小文件問題,除了HDFS,沒有其他的依賴。

雖然HAR文件減少了NameNode中小文件對內存的占用,但訪問HAR文件內容性能可能會更低。HAR文件仍然隨機存儲在磁盤上,并且讀取HAR內的文件需要訪問兩個索引 - 一個用于NameNode找到HAR文件本身,一個用于在HAR文件內找到小文件的位置。在HAR中讀取文件實際上可能比讀取存儲在HDFS上的相同文件慢。MapReduce作業(yè)的性能同樣會受到影響,因為它仍舊會為每個HAR文件中的每個文件啟動一個map任務。

所以這里我們需要有一個權衡(trade-off),HAR文件可以解決NameNode內存問題,但同時會降低讀取性能。如果你的小文件主要用于存檔,并且不經常訪問,那么HAR文件是一個很好的解決方案。如果小文件經常要被讀取或者處理,那么可能需要重新考慮解決方案。

2. NameNode聯(lián)邦

NameNode聯(lián)邦允許你在一個集群中擁有多個NameNode,每個NameNode都存儲元數據對象的子集。這樣可以讓所有的元數據對象都不止存儲在單個機器上,也消除了單個節(jié)點的內存限制,因為你可以擴容。這聽上去是一個很美麗的方案,但其實它也有局限性。


NameNode聯(lián)邦隔離了元數據對象 - 僅僅只有某一個NameNode知道某一個特定的元數據對象在哪里,意思就是說如果你想找到某個文件,你必須知道它是保存在哪個NameNode上的。如果你的集群中有多個租戶和或隔離的應用程序,那使用NameNode聯(lián)邦是挺不錯的,你可以通過租戶或者應用程序來隔離元數據對象。但是,如果要在所有的應用程序之間共享數據,則該方法其實也并不是完美的。

6.解決MR的性能問題

根據之前討論的內容,MapReduce性能問題主要是由隨機磁盤IO和啟動/管理太多的map任務組合引起的。解決方案似乎很明顯 - 合并小文件,以下討論一下幾種解決方案:
注:雖然本章名為解決MapReduce的性能問題,但其實同樣也是為了解決NameNode的壓力,以及解決其他計算引擎比如Impala/Spark的性能問題。

1. 修改數據抽取方法/間隔

解決小文件問題的最簡單方法就是在生成階段就進行杜絕(在源頭拒絕小文件的產生)。
如果是由數據源產生大量小文件并直接拷貝到Hadoop,可以調研了解數據源是否能生成一些大文件,或者從數據源到HDFS的數據抽取過程中進行數據處理合并小文件。如果每小時只抽取10MB的數據,考慮是否改為每天一次,這樣創(chuàng)建1個240MB的文件而不是24個10MB的文件。
但是,可能無法控制數據源的改動配合或業(yè)務對數據抽取間隔的需求,這樣小文件問題無法避免,這時可能需要考慮其他的解決方案。
————————————————

2. 批量文件合并

當產生小文件是不可避免時,文件合并是常見的解決方案。使用這種方法,定期運行一個MapReduce任務,讀取某一個文件夾中的所有小文件,并將它們重寫為較少數量的大文件。
比如一個文件夾中有1000個文件,你可以在一個MapReduce任務中指定reduce的數量為5,這樣1000個輸入文件會被合并為5個文件。隨后進行一些簡單的HDFS文件/文件夾操作(將新文件覆蓋回原目錄),則可以將NameNode的內存使用減少到200分之1,并且可以提高以后MapReduce或其他計算引擎對同一數據處理的性能。

在Hive或Java MapReduce中實現(xiàn)比較容易。這些MapReduce作業(yè)運行同樣需要集群資源,所以建議調度在生產系統(tǒng)非繁忙時間段執(zhí)行。但是,應該定期執(zhí)行這種合并的MapReduce作業(yè),因為小文件隨時或者幾乎每天都可能產生。

但這個合并程序需要有額外的邏輯來判斷存在大量小文件的目錄,或者你自己是知道哪些目錄是存在大量小文件的。因為假如某個目錄只有3個文件,運行合并作業(yè)遠不如合并一個500個文件的文件夾的性能優(yōu)勢提升明顯。

批量合并文件的方法無法保留原始文件名,如果原始文件名對于你了解數據來源非常重要,則批量合并文件的方法也不適用。
————————————————

3. 使用CombineFileInputFormat

CombineFileInputFormat是Hadoop提供的抽象類,它在MapReduce讀取時合并小文件。合并的文件不會持久化到磁盤,它是在一個map任務中合并讀取到的這些小文件。

好處是MapReduce可以不用為每個小文件啟動一個map任務,而且因為是自帶的實現(xiàn)類,你用額外將小文件先提前合并。這解決了MapReduce作業(yè)啟動太多map任務的問題,但是因為作業(yè)仍然在讀取多個小文件,隨機磁盤IO依舊是一個問題

因為合并的文件并不會持久化保存到磁盤,因此CombineFileInputFormat方式并不會緩解NameNode內存管理問題。只是提高MapReduce或者Hive作業(yè)的性能。
————————————————

4. HBase列式存儲

解決小文件問題,除了HDFS存儲外,當然還可以考慮HBase列式存儲。使用HBase可以將數據抽取過程從生成大量小HDFS文件更改為以逐條記錄寫入到HBase表。如果你對數據訪問的需求主要是隨機查找或者叫點查,則HBase是最好的選擇。HBase在架構上就是為快速插入,存儲大量數據,單個記錄的快速查找以及流式數據處理而設計的。但如果你對數據訪問的需求主要是全表掃描,則HBase不是最適合的。

使用HBase,可以較好的應對實時數據寫入以及實時查詢的場景。但是如何分配和平衡HBase與集群上其他的組件的資源使用,以及HBase本身運維都會帶來額外的運維管理成本。另外,HBase的性能主要取決于你的數據訪問方式,所以在選擇HBase解決小文件問題之前,應該進行仔細調研和設計。

推薦閱讀:

https://www.cnblogs.com/ballwql/p/8944025.html
https://blog.csdn.net/u010010664/article/details/83105174

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容