《Hadoop权威指南》学习笔记(7)——Hadoop的I/O操作(3)

这是Hadoop的I/O操作的最后一部分了。
这一部分的主要内容是用来存储文件数据的数据结构。

关于SequenceFile

Hadoop 的SequenceFile类为二进制键值对提供了一个持久数据结构,对于不适合用纯文本记录的二进制类型日志文件非常合适。
HDFS 和 MapReduce 是针对大文件优化的,所以通过SequenceFIle类将小文件包装起来,可以获得更高效率的存储和处理。

SequenceFile的写操作

通过createWriter()方法可以创建SequenceFile对象,并返回SequenceFIle.Writer实例。该静态方法需要指定待写入的数据流(FSDataOutputStreamFileSystem对象和Path对象),Configuration对象,以及键和值的类型。另外,可选参数包括压缩类型以及相应的codecProgressable回调函数用于通知写入的进度,以及在SequenceFile头文件中存储的Metadata实例。存储在SequenceFile中的键和值不一定必须是Writable类型,只要能被Serialization序列化和反序列化,任何类型都可以。

IntWritable key = ...
Text value = ...
SequenceFile.Writer writer = null;
try{
	writer = SequenceFile.createWriter(fs, conf, path, key.getClass(), value.getClass());
	writer.append(key, value);
}else{
	IOUtils.closeStream(writer);
}

一旦拥有了SequenceFIle.Writer实例,就可以通过append()方法在文件末尾附加键值对,写完后,可以调用close()方法。

SequenceFile的读操作

从头到尾读取顺序文件的方法是创建SequenceFile.Reader实例后反复调用next()方法迭代读取记录。读取哪条记录与使用的序列化框架相关。若用的是Writable类型,那么通过键和值作为参数的next()方法可以将数据流中的下一条键值对读入变量中:

public boolean next(Writable key, Writable val)

若键值对成功读取,则返回true,若已到文件末尾,则返回false

对于其他非Writable类的序列化框架,则应用下面两个方法:

public Object next(Object key) throws IOException
public Object getCurrentValue(Object val) throws IOException

这种情况下,要将io.serializations属性设置为想使用的序列化框架。
若next()方法返回的是非null对象,则可以从数据流中读取键值对并可以通过getCurrentValue()方法读取该值。若next()返回null对象,则说明已读到文件尾。

IntWritable key = ...
Text value = ...
SequenceFile.Reader reader = null;
long position = reader.getPosition();
try{
	reader = new SequenceFile.Reader(fs, path, conf);
	Writable key = (Writable)ReflectioonUtils.newInstance(reader.getKeyClass(), conf);
	Writable value = (Writable)ReflectioonUtils.newInstance(reader.getValueClass(), conf);
	while(reader.nect(key, value){
		String syncSeen = reader.syncSeen() ? "*" : "";
		......
		position = reader.getPosition();//找到下一个记录的位置
	}
}else{
	IOUtils.closeStream(reader);
}

程序中的syncSeen用来表示当前记录是否包括文件中的同步点的位置信息。
所谓同步点,是指数据读取迷路后可以再一次与记录边界同步的数据流中的某个位置。同步点是由SequenceFile.Writer记录的,后者在顺序文件写入过程中插入一个特殊项以便每隔几个记录就有一个同步标识。

如果想要在顺序文件中上搜索给定位置的记录,有两种方法:
①调用seek()方法,该方法将读指针指向文件中指定的位置:

reader.seek(...)

但若给定位置不是记录边界,则会抛出IOException异常。
②通过同步点查找记录边界。SequenceFile.Reader对象的sync(long position)方法可以将读取位置定位到 position 后的下一个同步点。

命令行接口显示SequenceFile

hadoop fs 命令有一个-text选项可以以文本形式显示顺序文件。该选项可以查看文件代码,由此检测出文件类型并将其转化成相应文本,该选项可以识别 gzip 压缩文件、顺序文件和 Avro 数据文件。
对于顺序文件,如果键和值是由具体含义的字符串,那么这个命令就很有用。
命令如下:

hadoop fs -text numbers.seq | head
SequenceFile的排序和合并

①通过 MapReduce 实现排序归并,MapReduce 是对多个顺序文件进行排序或合并的最有效的方法。
②使用SequenceFile.Sorter类中的sort()方法和merge()方法,但它比 MapReduce 更底层(如,实现并行需要手动对数据进行分区)。
所以 MapReduce 是更好的选择。

SequenceFile的格式

顺序文件由文件头和随后的一条或多条记录组成,其中同步标识位于顺序文件中的记录与记录之间:
顺序文件结构
同步标识的额外存储开销要求小于1%,所以没有必要在每条记录末尾都添加。

记录的内部结构取决于是否启用压缩,若已启用压缩,则结构取决于是记录压缩还是数据块压缩。
①没有启用压缩(默认情况)。每条记录由记录长度(字节数)、键长度、键和值组成,记录长度字段为四字节长的正数,遵循java.io.DataOutput类中writeInt()方法的协定。为写入顺序文件的类定义Serialization类,通过它来实现键和值的序列化。
未启用压缩的顺序文件内部结构
②记录压缩。与无压缩情况基本相同,只不过值是用文件头中定义的codec压缩。
记录压缩的顺序文件内部结构
③块压缩。块压缩是指一次性压缩多条记录,因为它可以利用记录间的相似性进行压缩,所以相较于单条记录压缩方法,该方法压缩效率更高。
块压缩的顺序文件内部结构

关于MapFile

MapFile 是已经排过序的SequenceFile,它有索引,所以可以按键查找。索引自身就是一个SequenceFile,包含了 map 中的一小部分键。由于索引能加载进内存,因此可以提供对主数据文件的快速查找。主数据文件则是另一个SequenceFile,包含了所有的map 条目,这些条目按键顺序进行了排序。
MapFile提供了一个用于读写的、与SequenceFile类似的接口。需要注意的是,当使用MapFile.Writer进行写操作时,map 条目顺序添加,否则会抛出IOException异常。

MapFile的变种

SetFileSetFile是一个特殊的MapFile,用于存储Writable键的集合。键必须按排好的顺序添加。
ArrayFile。该变种中的键是一个整型,用于表示数组中元素的索引,而值是一个Writable值。
BloomMapFile。该变种提供了get()方法的一个高性能实现,对稀疏文件特别有用。该实现使用一个动态的布隆过滤器来检测某个给定的键是否在 map 文件中。虽然速度很快,但这个测试会有出现假阳性的可能。所以仅当测试通过时(键存在),常规的get()方法才会被调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值