小文件指的是那些size比HDFS 的block size(默認64M)小不少的文件。而HDFS的問題在於沒法頗有效的處理大量小文件。node
任何一個文件,目錄和block,在HDFS中都會被表示爲一個object存儲在namenode的內存中,每個object佔用150 bytes的內存空間。因此,若是有10million個文件,每個文件對應一個block,那麼就將要消耗namenode 3G的內存來保存這些block的信息。若是規模再大一些,那麼將會超出現階段計算機硬件所能知足的極限。app
不只如此,HDFS並非爲了有效的處理大量小文件而存在的。它主要是爲了流式的訪問大文件而設計的。對小文件的讀取一般會形成大量從datanode到datanode的seeks和hopping來retrieve文件,而這樣是很是的低效的一種訪問方式。jvm
大量小文件在mapreduce中的問題ide
Map tasks一般是每次處理一個block的input(默認使用FileInputFormat)。若是文件很是的小,而且擁有大量的這種小文件,那麼每個map task都僅僅處理了很是小的input數據,而且會產生大量的map tasks,每個map task都會消耗必定量的bookkeeping的資源。比較一個1GB的文件,默認block size爲64M,和1Gb的文件,沒一個文件100KB,那麼後者沒一個小文件使用一個map task,那麼job的時間將會十倍甚至百倍慢於前者。oop
hadoop中有一些特性能夠用來減輕這種問題:能夠在一個JVM中容許task reuse,以支持在一個JVM中運行多個map task,以此來減小一些JVM的啓動消耗(經過設置mapred.job.reuse.jvm.num.tasks屬性,默認爲1,-1爲無限制)。另外一種方法爲使用MultiFileInputSplit,它可使得一個map中可以處理多個split。post
爲何會產生大量的小文件?this
至少有兩種狀況下會產生大量的小文件設計
1. 這些小文件都是一個大的邏輯文件的pieces。因爲HDFS僅僅在不久前纔剛剛支持對文件的append,所以之前用來向unbounde files(例如log文件)添加內容的方式都是經過將這些數據用許多chunks的方式寫入HDFS中。orm
2. 文件自己就是很小。例如許許多多的小圖片文件。每個圖片都是一個獨立的文件。而且沒有一種頗有效的方法來將這些文件合併爲一個大的文件。圖片
這兩種狀況須要有不一樣的解決方 式。對於第一種狀況,文件是由許許多多的records組成的,那麼能夠經過件邪行的調用HDFS的sync()方法和append方法結合使用來解 決。或者,能夠經過些一個程序來專門合併這些小文件(see Nathan Marz’s post about a tool called the Consolidator which does exactly this).
對於第二種狀況,就須要某種形式的容器來經過某種方式來group這些file。hadoop提供了一些選擇:
* HAR files
Hadoop Archives (HAR files)是在0.18.0版本中引入的,它的出現就是爲了緩解大量小文件消耗namenode內存的問題。HAR文件是經過在HDFS上構建一個層次化的文件系統來工做。一個HAR文件是經過hadoop的archive命令來建立,而這個命令實 際上也是運行了一個MapReduce任務來將小文件打包成HAR。對於client端來講,使用HAR文件沒有任何影響。全部的原始文件都 visible && accessible(using har://URL)。但在HDFS端它內部的文件數減小了。
通 過HAR來讀取一個文件並不會比直接從HDFS中讀取文件高效,並且實際上可能還會稍微低效一點,由於對每個HAR文件的訪問都須要完成兩層index 文件的讀取和文件自己數據的讀取(見上圖)。而且儘管HAR文件能夠被用來做爲MapReduce job的input,可是並無特殊的方法來使maps將HAR文件中打包的文件看成一個HDFS文件處理。 能夠考慮經過建立一種input format,利用HAR文件的優點來提升MapReduce的效率,可是目前尚未人做這種input format。 須要注意的是:MultiFileInputSplit,即便在HADOOP-4565的改進(choose files in a split that are node local),但始終仍是須要seek per small file。
* Sequence Files
通 常對於「the small files problem」的迴應會是:使用SequenceFile。這種方法是說,使用filename做爲key,而且file contents做爲value。實踐中這種方式很是管用。回到10000個100KB的文件,能夠寫一個程序來將這些小文件寫入到一個單獨的 SequenceFile中去,而後就能夠在一個streaming fashion(directly or using mapreduce)中來使用這個sequenceFile。不只如此,SequenceFiles也是splittable的,因此mapreduce 能夠break them into chunks,而且分別的被獨立的處理。和HAR不一樣的是,這種方式還支持壓縮。block的壓縮在許多狀況下都是最好的選擇,由於它將多個 records壓縮到一塊兒,而不是一個record一個壓縮。
將已有的許多小文件轉換成一個SequenceFiles可能會比較慢。但 是,徹底有可能經過並行的方式來建立一個一系列的SequenceFiles。(Stuart Sierra has written a very useful post about converting a tar file into a SequenceFile — tools like this are very useful).更進一步,若是有可能最好設計本身的數據pipeline來將數據直接寫入一個SequenceFile。