你好,游客 登录
背景:
阅读新闻

Hadoop特定的文件类型

[日期:2014-09-24] 来源:csdn  作者:blogdevteam [字体: ]

除“普通”文件之外,HDFS还引入了一些特定的文件类型(例如SequenceFile、MapFile、SetFile、ArrayFile和BloomMapFile),它们提供更加丰富的功能,且通常会简化数据处理。

SequenceFile提供了用于二进制键/值对的持久化数据结构。这里,键和值的不同实例必须代表相同的Java类,但大小可以不同。类似于其他hadoop文件,SequenceFile只能追加。

当使用普通(文本或二进制)文件保存键/值对(MapReduce使用的典型数据结构)时,数据存储并不知道键和值的布局,它必须在通用存储之上的 读取器中实现。SequenceFile的使用提供了一种存储机制,原生支持键/值结构,从而使用了这种数据布局的实现更加简单。

SequenceFile有三种可用格式:无压缩、记录压缩和块压缩。前两种以基于记录的格式保存(如图2-2所示),而第三种使用基于块的格式(如图2-3所示)。

 

对于序列文件来说,特定格式的选取决定了该文件在硬盘驱动器上的大小。块压缩的文件通常最小,而无压缩的最大。

在图2-2和图2-3中,头部(header)包含了有关SequenceFile的一般信息,如表2-1所示。
表2-1  SequenceFile头部(header)


字    段 描    述
Version 一个4字节的数组,包含三个字符(SEQ)和一个序列文件版本号(4或6)。当前使用的是版本6。为向后兼容而支持版本4
Key Class 键的类名,使用读取器提供的键的类名对其进行验证
Value Class 值的类名,使用读取器提供的值的类名对其进行验证
Compression 键/值压缩标志
Block Compression 块压缩标志
Compression Codec CompressionCodec类。该类仅在键/值或块压缩标志为true时使用。否则,该值被忽略
Metadata 元数据(可选)是一个键/值对列表,可以用于向文件添加用户特定的信息
Sync sync标识符

注意:

同步(sync)是个专门的标记,用于在SequenceFile内部更快速地进行查找。sync标记在MapReduce实现中还有一个特殊作用——数据分割只能在sync边界处进行。

如表2-2所示,记录中包含了键和值的实际数据,以及它们的长度。
表2-2  记录布局


字    段 描    述
Record Length 记录的长度(字节)
Key Length 键的长度(字节)
Key 字节数组,包含记录的键
Value 字节数组,包含记录的值

在基于块的情况下,header和sync服务于相同的目的(与基于记录的SequenceFile格式的情况相同)。实际的数据包含在块中,如表2-3所示。
表2-3  块布局


字    段 描    述
Keys Lengths Length 在这种情况下,某个给定块中的所有键都保存在一起。该字段指定压缩后的键-长度大小(以字节为单位)
Keys Lengths 字节数组,包含压缩的键-长度块
Keys Length 压缩后的键大小(以字节为单位)
Keys 字节数组,包含块中压缩后的键
Values Lengths Length 在这种情况下,某个给定块中的所有值都保存在一起。该字段指定压缩后的值-长度大小(以字节为单位)
Values Lengths 字节数组,包含压缩的值-长度块
Values Length 压缩后的值大小(以字节为单位)
Values 字节数组,包含块中压缩后的值

所有格式均使用相同的header,其中包含着可以由读取器识别的信息。header(参见表2-1)包含了键和值的类名(被读取器用来实例化这些 类)、版本号以及压缩信息。如果启用了压缩,则header中会增加Compression Codec class name字段。

SequenceFile的元数据是一系列键/值文本对,可以包含关于SequenceFile的额外信息,文件读取器/写入器会使用这些信息。

无压缩格式和记录压缩格式的写操作的实现非常类似。每一次对append()方法的调用都会向SequenceFile添加一条记录,其中包含整条 记录的长度(键的长度加值的长度)、键的长度以及键和值的原始数据。压缩和无压缩版本之间的不同在于是否使用特定的编解码器对原始数据进行了压缩。

块压缩格式可以达到更高的压缩率。直到达到某个阈值(块大小)后,数据才会被写入,此
所有的键将被一起压缩。值以及键和值长度的列表也会被压缩。

Hadoop提供了用于SequenceFiles的特殊读取器(SequenceFile.Reader)和写入器(SequenceFile.Writer)。代码清单2-3展示了使用SequenceFile.Writer的一小段代码。

代码清单2-3:使用SequenceFile.Writer
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(conf);
Path path = new Path("fileName");
SequenceFile.Writer sequenceWriter = new SequenceFile.Writer(fs, conf, path,
Key.class,value.class,fs.getConf().getInt("io.file.buffer.size",
4096),fs.getDefaultReplication(), 1073741824, null,new Metadata());
……………………………………………………………..
sequenceWriter.append(bytesWritable, bytesWritable);
…………………………………………….......
IOUtils.closeStream(sequenceWriter);

一个最简化的SequenceFile写入器构造函数(SequenceFile.Writer(FileSystem fs, Configuration conf, Path name, Class keyClass, Class valClass))需要文件系统的类型、Hadoop配置、路径(文件位置)以及键和值的类定义。前面示例中使用的构造函数支持指定额外的文件参数,包括以下几个:

int bufferSize——如果不定义,则使用默认的缓冲区大小(4096)。

short replication——使用默认复制。

long blockSize——使用值1073741824(1024MB)。

Progressable progress——使用None。

SequenceFile.Metadata metadata——使用一个空的元数据类。

写入器一旦创建完成,就可以用来向文件添加键/记录对了。

SequenceFile的局限之一是无法基于键值进行查找。其他Hadoop文件类型(MapFile、SetFile、ArrayFile和 BloomMapFile)通过在SequenceFile之上增加基于键的索引克服了这个限制。如图2-4所示,MapFile实际上并非文件,而是目 录,其中包含两个文件——一个数据(序列)文件,包含map中所有的键和值;一个较小的索引文件,包含一部分键。可以通过按顺序添加条目来创建 MapFile。MapFile通常利用其索引来高效地搜索和检索文件的内容。

 

索引文件中包含键和一个LongWritable对象,LongWritable对象保存了该键对应记录的起始字节位置。索引文件并不包含所有键, 而是只包含其中一部分。我们可以使用写入器的setIndexInterval()方法设置indexInterval。索引会被完全读入内存,因此对于 很大的map,有必要设置索引跳跃值,使得索引文件足够小,以致能够完全加载到内存中。

类似于SequenceFile,Hadoop提供了用于map文件的特殊读取器(MapFile.Reader)和写入器(MapFile.Writer)。

SetFile和ArrayFile是用于实现特定键/值类型的MapFile变体。SetFile是一种MapFile,用于代表没有值的键集合 (值由NullWritable实例来代表)。ArrayFile处理键/值对,其键为连续的long(长整型)值。它维护一个内部计数器,并在每次进行 追加调用时增加。该计数器的值会被用作键。

这两种文件类型对于保存键(而非值)很有用。

布隆过滤器

布隆过滤器是一种空间利用率高的、概率性的数据结构,用于测试一个元素是否是某个集合的成员。测试的结果是该元素确定不在集合中,或者可能在集合中。

布隆过滤器的基础数据结构是比特向量。误报的可能性取决于元素集合的大小和比特向量的大小。

尽管会有误报,但布隆过滤器在表示集合时相比于其他数据结构(例如自平衡二叉搜索树、单词查找树、哈希表或者简单的数组或链表)有着很强的空间优 势。大多数数据结构至少要保存数据条目本身,这需要的存储空间可能是从少量比特位(对于小整数)到任意数量的比特位,例如对于字符串(单词查找树是个特 例,因为前缀相同的元素之间可以共享存储)。

布隆过滤器的这种优势一部分源于其紧凑性(继承自数组),还有一部分源于其概率性本质。

最后,BloomMapFile通过添加动态的布隆过滤器(参见补充内容“布隆过滤器”)扩展了MapFile实现,为键提供快速的成员资格测试。 它还提供了键搜索操作的一个快速版本,尤其适用于稀疏的MapFile。写入器的append()操作会更新DynamicBloomFilter,当写 入器关闭时,DynamicBloomFilter会被序列化。当创建读取器时,该过滤器会被加载到内存中。读取器的get()操作首先利用过滤器检查键 的成员资格,如果键不存在的话,它立即返回空值,不再进行任何进一步的I/O操作。

数据压缩

在HDFS文件中存储数据时,一个需要考虑的重要因素就是数据压缩——将数据处理中的计算负载从I/O转化为CPU。一些出版物提供了对在MapReduce实现中使用压缩时,计算与I/O之间相互权衡的系统评估,其结果显示数据压缩的益处取决于数据处理作业的类型。对于大量读操作(I/O是瓶颈)应用(例如,文本数据处理),压缩会节省35%~60%的性能开销。另一方面,对于计算密集型应用(CPU是瓶颈),数据压缩带来的性能提升微不足道。

但这并不意味着数据压缩对此类应用没有益处。Hadoop集群的资源均是共享的,其结果是
一个应用I/O负载的降低将会提升使用该I/O的其他应用的性能。

这意味着总是需要数据压缩吗?答案是“否”。例如,如果正在使用文本文件或自定义二进制输入文件,那么可能就不需要数据压缩,因为压缩后的文件不能 被分割(在第3章中会学到更多)。另一方面,对于SequenceFile及其衍生的文件类型,压缩总是需要的。最后,压缩用于shuffle和sort 的中间文件总是有意义的(在第3章中会学到更多)。
记住数据压缩的结果在很大程度上取决于待压缩数据的类型和压缩算法。





收藏 推荐 打印 | 录入: | 阅读:
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数
点评:
       
评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款