小文件是指那些size比HDFS的block size(默認64m)小的多的文件。任何一個文件,目錄和bolck,在HDFS中都會被表示為一個object存儲在namenode的內存中,每一個object占用150bytes的內存空間。所以,如果有10milion個文件,每一個文件對應一個block,那么就會消耗namenode 3G來保存這些block的信息。如果規模再大一點,那么將會超出現階段計算機硬件所能滿足的極限。
控制小文件的方法有:
1應用程序自己控制
2archieve
第一種是我采用的方法,感覺使用起來還是比較方便的,我的需求是要對幾千個文件進行分布式運算,每個文件占用的空間是2m左右,如果不進行合並的話,那樣子運行效率太低了,這里我打算把50個小文件合並為一個大文件放到hdfs系統里面進行運算,代碼如下:
final File dir=new File(/home/user/mapinput"); int filename=0; while(dir.listFiles().length!-0){ Path path=new Path("/input/+filename); FSDataOutputStream create=fs.creat(path); int num=0; for(File fileName:dir.listFiles()){ System.out.println(fileName.getAbsolutePath()); final FileInputStream fileInputStream=new FileInputStream(fileName.getAbsolutePath()); final List<String> readLines=IOUtils.readLines(fileInputStream); for(String line=readLines) { create.write(line.getBytes()); create.write('\n'); } fileInputStream.close(); File f=new File("/home/user/mapinput/"+fileName); if(fileName.exists())fileName.delete(); mun++; if(num==50){ break; } } filename++; create.close(); }
這樣,原本幾千個小文件就變成了若干個100m左右的文件了,文件的大小可以通過參數num的數目來決定。
2使用Archive來操作
hadoop不適合小文件的存儲,小文件本省就占用了很多的metadata,就會造成namenode越來越大。Hadoop Archives的出現視為了緩解大量小文件消耗namenode內存的問題。
通過HAR來讀取一個文件並不會比直接從HDFS中讀文件高效,而且實際上可能還會稍微低效一點,因為對每一個HAR文件的訪問都需要完成兩層讀取,index文件的讀取和文件本身的讀取,而且盡管HAR文件可以被用來作為mapreduce job的input,但是並沒有特殊的方法來使maps將HAR文件中打包的文件當做一個HDFS文件處理。
命令:
hadoop archive -archiveName user.har -p /user output /user/har
查看內容:hadoop fs -lsr har:///user/har/user.har