SequenceFile与MapFile &hdfs的主要API类--FileSystem FSDataInputStream

     HDFS和MR主要针对大数据文件来设计,在小文件处理上效率低.解决方法是选择一个容器,将这些小文件包装起来,将整个文件作为一条记录,可以获取更高效率的储存和处理,避免多次打开关闭流耗费计算资源.hdfs提供了两种类型的容器 SequenceFile和MapFile


一、SequenceFile


SequenceFile的存储类似于Log文件,所不同的是Log File的每条记录的是纯文本数据,而SequenceFile的每条记录是可序列化的字符数组。

SequenceFile可通过如下API来完成新记录的添加操作:

        fileWriter.append(key,value)

可以看到,每条记录以键值对的方式进行组织,但前提是Key和Value需具备序列化和反序列化的功能

hadoop预定义了一些Key Class和Value Class,他们直接或间接实现了Writable接口,满足了该功能,包括:

Text              等同于Java中的String
IntWritable          等同于Java中的Int
BooleanWritable        等同于Java中的Boolean
        .
        .

在存储结构上,SequenceFile主要由一个Header后跟多条Record组成,如图所示:

Header主要包含了Key classname,Value classname,存储压缩算法,用户自定义元数据等信息,此外,还包含了一些同步标识,用于快速定位到记录的边界。

每条Record以键值对的方式进行存储,用来表示它的字符数组可依次解析成:记录的长度、Key的长度、Key值和Value值,并且Value值的结构取决于该记录是否被压缩。

数据压缩有利于节省磁盘空间和加快网络传输,SeqeunceFile支持两种格式的数据压缩,分别是:record compression和block compression。

record compression如上图所示,是对每条记录的value进行压缩

block compression是将一连串的record组织到一起,统一压缩成一个block,如图所示:

block信息主要存储了:块所包含的记录数、每条记录Key长度的集合、每条记录Key值的集合、每条记录Value长度的集合和每条记录Value值的集合

注:每个block的大小是可通过io.seqfile.compress.blocksize属性来指定的


二、MapFile

MapFile是排序后的SequenceFile,通过观察其目录结构可以看到MapFile由两部分组成,分别是data和index。

index作为文件的数据索引,主要记录了每个Record的key值,以及该Record在文件中的偏移位置。在MapFile被访问的时候,索引文件会被加载到内存,通过索引映射关系可迅速定位到指定Record所在文件位置,因此,相对SequenceFile而言,MapFile的检索效率是高效的,缺点是会消耗一部分内存来存储index数据。

需注意的是,MapFile并不会把所有Record都记录到index中去,默认情况下每隔128条记录存储一个索引映射。当然,记录间隔可人为修改,通过MapFIle.Writer的setIndexInterval()方法,或修改io.map.index.interval属性;

另外,与SequenceFile不同的是,MapFile的KeyClass一定要实现WritableComparable接口,即Key值是可比较的。


总结:

SequenceFile文件是用来存储key-value数据的,但它并不保证这些存储的key-value是有序的,

而MapFile文件则可以看做是存储有序key-value的SequenceFile文件。

MapFile文件保证key-value的有序(基于key)是通过每一次写入key-value时的检查机制,这种检查机制其实很简单,就是保证当前正要写入的key-value与上一个刚写入的key-value符合设定的顺序,

但是,这种有序是由用户来保证的,一旦写入的key-value不符合key的非递减顺序,则会直接报错而不是自动的去对输入的key-value排序


Hadoop HDFS JAVA API

org.apache.hadoop.fs.FileSystem是在分布式环境中访问和管理HDFS中的文件/目录的通用类,文件内容以多个大尺寸块(64M)的形式存储在datanode中,namenode中记录了这些块的信息和文件的元信息。FileSystem可以读或者按块的顺序流式的访问。FileSystem首先从NameNode中得到块的信息,然后一个接一个的读。它打开第一个块,它将读完关闭后才访问下一块。HDFS的复制块带来了更高的可靠性和可扩展性。如果client是数据节点中的一个,它将先访问本地,如果失败的话,它才会到集群的其他节点去访问。

FileSystem 使用FSDataOutputStream 及FSDataInputStream来读写流的内容。Hadoop提供了多种FileSystem的实现:

  • DistributedFileSystem(DFS):在分布式的环境中,访问HDFS文件
  • LocalFileSystem:在本地系统中访问HDFS文件
  • FTPFileSystem:访问HDFS文件的FTP客户端
  • WebHdfsFileSystem:通过web接口访问HDFS文件。
URI及Path:

Hadoop的URI指定的HDFS的文件地址,FileSystem使用hdfs://host:port/location的形式来访问文件。

hdfs://localhost:9000/user/joe/TestFile.txt
URI uri=URI.create (“hdfs://host: port/path”);

其中uri中的host及port是在core-site.xml文件中定义的:

<property>
  <name>fs.defaultFS</name>
  <value>hdfs://localhost:9000</value>
</property>

Path应当如下的方法来产生:

Path path=new Path (uri); //It constitute URI
Configuration

Configuration类可以把Hadoop的配置信息传递给FileSystem.他默认通过类引导器引导core-site及core-default.xml,保存Hadoop配置信息 fs.defaultFS,fs.default.name等:

你可以用以下的方式来产生配置信息:

Configuration conf = new Configuration ();

你也可以明确的设置配置参数信息:

conf.set("fs.default.name", “hdfs://localhost:9000”);
FileSystem

下面的代码描述了怎样产生Hadoop的文件系统:

public static FileSystem get(Configuration conf)
public static FileSystem get(URI uri, Configuration conf)
public static FileSystem get(URI uri, Configuration conf, String user)

FileSystem使用NameNode来定位数据在DataNode的存放,并直接访问DataNode块,并按顺序读文件。FileSystem 使用Java IO FileSystem接口,主要是用DataInputStream 及 DataOutputStream的IO操作。

访问本地的文件系统,你可以直接使用getLocal方法:

public static LocalFileSystem getLocal(Configuration conf)
FSDataInputStream

FSDataInputStream封装了DataInputStream,实现了Seekable, PositionedReadable接口,用于提供getPos(), seek(),以提供对HDFS文件的随机访问方法。FileSystem的open()方法返回FSDataInputStream

URI uri = URI.create (“hdfs://host: port/file path”);
Configuration conf = new Configuration ();
FileSystem file = FileSystem.get (uri, conf);
FSDataInputStream in = file.open(new Path(uri));

上面的方法的FSDataInputStream缺省缓冲大小为4096byte,可以在产生输入流的时候定义缓冲的大小

public abstract FSDataInputStream open(Path path, int sizeBuffer)
public interface Seekable {
  void seek(long pos) throws IOException;
  long getPos() throws IOException;
 boolean seekToNewSource(long targetPos) throws IOException;
}

seek()方法找到从文件头到指定的长度的位置,read()从指定的位置以数据流的方式读数据,getPos()返回当前的InputStream流的位置

FileSystem file = FileSystem.get (uri, conf);

FSDataInputStream in = file.open(new Path(uri));
byte[] btbuffer = new byte[5];
in.seek(5); // sent to 5th position
Assert.assertEquals(5, in.getPos());
in.read(btbuffer, 0, 5);//read 5 byte in byte array from offset 0
System.out.println(new String(btbuffer));// &amp;amp;amp;quot; print 5 character from 5th position
in.read(10,btbuffer, 0, 5);// print 5 character staring from 10th position

FSDataInputStream也实现了 PositionedReadable,提供了read, readFully,或者读指定位置固定长度的值

read(long position, byte[] buffer, int offset, int length)
FSDataOutputStream

Filesystem的create()方法返回FSDataOutputStream,用于产生新的HDFS文件或写内容到EOF 它没有提供seek,因为HDFS限制了内容只能写到EOF。它包装了Java IO的DataOutputStream,增加了方法getPos()得到文件的位置,以及write()将内容写在最后的位置。

public FSDataOutputStream create(Path f) /*create empty file.*/
public FSDataOutputStream append(Path f) /* will append existing file */

create方法传送Progressable接口,用于跟踪文件产生的状态。

public FSDataOutputStream create(Path f, Progressable progress)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值