hadoop hdfs 被設計用作海量數(shù)據(jù)存儲,適合存儲大文件,文件在hdfs中是以block的形式存儲的,在hadoop 1.x中秦效,hdfs block的默認大小為64m,而在hadoop 2.x中涎嚼,block的默認大小為128m阱州,可以在hdfs-site.xml文件中的dfs.block.size配置項修改默認的塊大小。文件由一個或多個block組成法梯,文件的元數(shù)據(jù)信息由namenode記錄苔货,因此如果hdfs存儲大量的小文件時,會占用大量的block以及namenode必須耗費大量內(nèi)存來記錄這些文件的元數(shù)據(jù),造成存儲空間浪費以及影響hdfs 集群的橫向擴展蒲赂。因此以下兩種方案可以用來處理hdfs 小文件的問題:
1.sequencefile
2.hadoop archives file
SequenceFile
sequencefile 由header和一個個記錄組成,header記錄著keyclass 類型刁憋,valueclass 類型滥嘴,壓縮信息以及用戶自定義的信息,記錄record存儲的是真正的數(shù)據(jù)并以key-value的格式進行存儲至耻,sequencefile文件按壓縮可分為無壓縮格式若皱,記錄壓縮格式和塊壓縮格式。無壓縮格式和記錄壓縮格式相似尘颓,唯一的區(qū)別是記錄壓縮格式是值壓縮走触,格式如下圖所示:
而塊壓縮是對record進行壓縮,一個塊由多個record組成疤苹,當一個record的大小達到io.seqfile.compress.blockseze 默認1000000字節(jié)時互广,可加入到塊中,格式如圖所示:
示例代碼
package com.zjc.spark;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.compress.DefaultCodec;
import org.apache.hadoop.util.LineReader;
import java.io.File;
import java.io.FileInputStream;
/**
* Created by zjc on 2018/11/14.
*/
public class sparkApplication1 {
static Configuration configuration = null;
static {
configuration = new Configuration();
configuration.set("fs.defaultFS", "hdfs://z-cluster");
configuration.set("dfs.nameservices", "z-cluster");
configuration.set("dfs.ha.namenodes.z-cluster", "nn1,nn2");
configuration.set("dfs.namenode.rpc-address.z-cluster.nn1", "192.168.1.22:8120");
configuration.set("dfs.namenode.rpc-address.z-cluster.nn2", "192.168.1.107:8120");
configuration.set("dfs.client.failover.proxy.provider.z-cluster", "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
configuration.set("hadoop.user.name", "hadoop4.27");
}
public static void main6(String[] args) {
IntWritable key = new IntWritable();
Text value = new Text();
SequenceFile.Writer out = null;
try {
FileSystem fileSystem = FileSystem.get(configuration);
out = SequenceFile.createWriter(configuration, SequenceFile.Writer.file(new Path("/testFile")), SequenceFile.Writer.keyClass(IntWritable.class), SequenceFile.Writer.valueClass(Text.class), SequenceFile.Writer.compression(SequenceFile.CompressionType.BLOCK, new DefaultCodec()));
for (int i = 0; i < 100; i++) {
key.set(100 - i);
value.set(DATA[i % DATA.length]);
out.append(key, value);
if (i % 20 == 0) {
out.sync();//每四百條記錄添加一個同步點
}
}
} catch (Exception e) {
System.out.println(e);
} finally {
IOUtils.closeStream(out);
}
}
public static void main18(String[] args) {
IntWritable key = new IntWritable();
Text value = new Text();
SequenceFile.Reader in = null;
try {
in = new SequenceFile.Reader(configuration, SequenceFile.Reader.file(new Path("/testFile")));
// in.sync(2129);
long position = in.getPosition();
while (in.next(key, value)) {
System.out.println("position:" + position + " key:" + key.get() + " value:" + value.toString());
position = in.getPosition();
}
} catch (Exception e) {
System.out.println(e);
} finally {
IOUtils.closeStream(in);
}
}
}
可通過mr將多個小文件合并成一個sequencefile文件卧土,但是sequencefile的缺點是不支持追加惫皱。
Hadoop Archives File
可通過hdfs shell命令將多個小文件創(chuàng)建為歸檔文件,歸檔示例:
創(chuàng)建歸檔文件
hadoop archive -archiveName foo.har -p /user/hadoop -r 3 dir1 dir2 /user/zoo
上面的例子使用 /user/hadoop 作為創(chuàng)建歸檔的相對歸檔目錄尤莺。/user/hadoop/dir1 和 /user/hadoop/dir2 目錄將會歸檔到 /user/zoo/foo.har 里面旅敷。歸檔操作并不會刪除輸入文件。如果你想在創(chuàng)建歸檔文件之后刪除這些輸入文件颤霎,你需要自己做媳谁。在這個例子中,因為我們指定了 -r 3友酱,那么副本因子為3將會被使用晴音。
查找文件
在 hadoop 檔案中查找文件就像在文件系統(tǒng)上執(zhí)行 ls 一樣簡單。在我們歸檔完 /user/hadoop/dir1 和 /user/hadoop/dir2 目錄粹污,如果我們想查看歸檔里面有哪些文件段多,你僅僅需要使用下面命令:
hdfs dfs -ls -R har:///user/zoo/foo.har/
要理解-p 參數(shù)的重要性,讓我們再看一遍上面的例子壮吩。 如果您只是在 hadoop 存檔上使用 ls(而不是lsr)
hdfs dfs -ls har:///user/zoo/foo.har
輸出如下:
har:///user/zoo/foo.har/dir1
har:///user/zoo/foo.har/dir2
您可以回憶一下使用以下命令創(chuàng)建存檔
hadoop archive -archiveName foo.har -p /user/hadoop dir1 dir2 /user/zoo
如果我們將上面命令修改為下:
hadoop archive -archiveName foo.har -p /user/ hadoop/dir1 hadoop/dir2 /user/zoo
那么在 Hadoop 歸檔上如下使用 ls 命令:
hdfs dfs -ls har:///user/zoo/foo.har
那么你會得到如下結果:
har:///user/zoo/foo.har/hadoop/dir1
har:///user/zoo/foo.har/hadoop/dir2
請注意进苍,已歸檔文件已相對于 /user/ 而不是/ user/hadoop 進行歸檔。
Hadoop Archives 和 MapReduce
在 MapReduce 中使用 Hadoop Archives 就像使用默認文件系統(tǒng)中的文件一樣簡單鸭叙。 如果我們在 HDFS 上的 /user/zoo/foo.har 路徑里面存儲了 Hadoop 歸檔文件觉啊,那么在 MapReduce 里面將它作為輸入文件可以使用 har:///user/zoo/foo.har。
Hadoop Archives 是根據(jù)索引文件對目標文件進行讀取沈贝,所以讀性能比正常讀取低下杠人。