Hadoop 权威指南学习笔记(三)

                                     第四章 I/O

4.1  数据完整性

检测数据是否损坏的常见措施是,在数据第一次引入系统时计算校验和(checksum) , 井在数据通过一个不可靠的通道进行传输时再次计算校验和,这样就能发现数据是否损坏。HDFS 会对写入的所有数据计算校验和,井在读取数据时验证校验和,用这种方式来校验数据的完整性。 datanode 负责在验证收到的数据后存储数据及其校验和,
客户端从 datanode 读取数据时,也会验证校验和,将它们与 datanode 中存储的校验和进行比较。

4.2  压缩

文件压缩有两大好处: 可以减少存储文件所需要的磁盘空间:可以加速数据在网络和磁盘上的传输,所有压缩算法都需要权衡空间/时间:压缩和解压缩速度更快,其代价通常是只能节省少量的空间。不同压缩工具有不同的压缩特性。 gzip 是一个通用的压缩工具,在空间/时间性能的权衡中,居于其他两个压缩方法之间。
对于大文件来说,不要使用不支持切分整个文件的压缩格式,会造成MapReduce效率低下。
尽管 MapReduce 应用读写的是未经压缩的数据,但如果对 map 阶段的中间输入进 行压缩,也可以获得不少好处。由于 map 任务的输出需要写到磁盘并通过网络传输到 reducer 节点,所以如果使用 LZ。这样的快速压缩方式,是可以获得性能提升的,因为需要传输的数据减少了

4.3  序列化 

序列化(serialization时,是指将结构化对象转化为字节流,以便在网络上传输或 写到磁盘进行永久存储。反序列化(deserialization) 是指将字节流转回结构化对象的
逆过程 。 序列化在分布式数据处理的两大领域经常出现:进程间通信和永久存储。我们希望存储格式比较紧凑(进而高效使用存储空间)、快速(进而读写数据的额外开销比较小)、可扩展(进而可以透明地读取老格式的数据)且可以互操作(进而可以使用不同的语言读写永久存储的数据)。

Hadoop 使用自己的序列化格式 Writable ,它格式紧凑,速度快,但很难用 Java 以外的语言进行扩展或使用。

 4.4  序列化框架 

Hadoop 有一个针对可替换序列化框架 (serialization framework) API 。一个序列化框架用一个 Ser ialization 实现(在 org.apache.hadoop. io.ser ializer 包)来表示。例如, WritableSer ialization 类是对 Writable 类型的 Seri alization 的实现。 Serialization 对象定义了从类型到 Serializer 实例(将对象转换为字节流)和Deserializer 实例(将字节流转换为对象)的映射方式。Avro是一个独立于编程语言的数据序列化系统。该项目是由 Doug Cutting(Hadoop 之父)创建的,旨在解决 Hadoop Writable 类型的不足:缺乏语言的可移植性。拥有一个可被多种语言(当前是 C++ , Java ,Python Ruby)处理的数据格式与绑定到单一语言的数据格式相比,前者更易于与公众共享数据集。允许其他编程语言能够读写数据,该类数据格式进行读写操作,会使其具有更好的特性。

Avro

Avro 为一系列对象指定一个对象容器格式一一类似于 Hadoop 的顺序文件。 Avro 数据文件包含元数据项,模式数据存储在其中,这使文件可以自我声明。 Avro 数据文件支持压缩,并且是可切分的,这对 MapReduce 的输入格式至关重要。
所有的类型, record 除外,均按照 Avro 规范中预先定义的规则来排序,这些规则不能被用户改写。但对于记录,你可以通过order 属性来控制排列顺序。它
有三个值: ascending( 默认值)、 descending( 反向顺序)或 ignore(所以为了比较的目的,可以忽略该字段。)

Avro 井不需要将二进制对象反序列化成对象即可实现比较,因为它可以直接对字节流进行操作。因为 Avro 数据文件只是一系列顺序排列的值。但是,为了混洗的目的依旧将中间结果数据划分为键/值对

基于文件的数据结构

考虑日志文件,其中每一条日志记录是一行文本。如果想记录二进制类型,纯文本是不合适的。这种情况下, Hadoop SequenceFile 类非常合适,因为上述类提 供了二进制键/值对的永久存储的数据结构。SequenceFiles 同样也可以作为小文件的容器。而 HDFS MapReduce 是针对大文件进行优化的,所以通过 SequenceFile 类型将小文件包装起来,可以获得更高效率的存储和处理。
 

1. SequenceFile 的写操作

存储在 SequenceFile 中的键和值并不一定需要时 Writable 类型。任一可以通过Serialization 类实现序列化和反序列化的类型均可被使用。通过 createWriter() 静态方法可以创建 SequenceFile 对象,并返回 SequenceFile.Writer 实例。该静态方法有多个重载版本,但都需要指定代写人的数据流 (FSDataOutputStream或FileSystem 对象和 Path 对象) , Configuration 对象,以及键和值的类型。

2. 读取 SequenceFile
 
从头到尾读取顺序文件的过程是创建 SequenceFile.Reader 实例后反复调用next() 方法 迭代读取记录的过程。读取的是哪条记录 与你使用的序列化框架相关 。如果你使用的是 wr itable 类型,那么通过键和值作为参数的 next() 方法可以将数据流中的下一条键值对读入变量中, 如果键值对成功读取,则返回 true ,如果己读到文件末尾,则返回 false. 对于非 Writable 类型的序列化框架(比如 Apache Thrift) ,则需要使用下述方法:
public Object next(Object key) throws IOException
public Object getCurrentValue(Object val) throws IOException
如果 next() 方怯返回的是非 -null 对象,则可以从数据流中读取键值对,井且可以通过 getCurrentValue() 方法读取该值。否则,如果 next ( )返回 null 值,则表示已经读到文件末尾。 所谓同步点是指当数据读取的实例出错后能够再一次与记录边界同步的数据流中的一个位置。
 
3. 排序
MapReduce 是对多个顺序文件进行排序(或合并)最有效的方法。 MapReduce 本身具有并行执行能力,并且可由你 指定 reducer 的数量(该数决定着输出文件数量)。
 
4.  SequenceFile的格式
顺序文件由文件头和随后的一条或多条记录组成(参见图 4-2) 。顺序文件的前三个字节为 SEQ( 顺序文件代码),紧随其后的一个字节表示 J@î 序文件的版本号。
记录的内部结构与是否启用压缩有关。块压缩一次对多条记录进行压缩,因此相较于单条记录压缩,压缩效率更高,因为可以利用记录间的相似性进行压缩。
可以不断向数据块中压缩记录, 直到块的字节数不小于 io.seqfile.compress.blocksize 属性中设置的字节数:默认为1 MB 。每一个新的块的开始处都需要插入同步标识。数据块的格式如 :首先是一个指示数据块中字节数的字段,紧接着是4个压缩字段(键长度、键、值长度和值。
 

2. MapFile

MapFile 是己经排序的 SequenceFile ,它己加入用于搜索键的索引。 MapFile 写入类似于 Seq uenceFile 的写入。 首先新建一个M apFile.Writer 实例,然后调用 append() 方法将条目顺序写入。如果不按顺序写入条目,将抛出一个 IOException 异常。默认情况下只有每隔 128 个键才有一个包含在 index 文件中, 当然也可以通过调用 MapFile. Wri ter 实例中的 setlndexlnterval() 方法来设置 io.map.index.interval 属性即可。增加索引间隔数量可以有效减少MapFile 中用于存储索引的内存。相反,可以降低该间隔来提高随机访问时间(因为减少了平均跳过的记录数) ,这是以提高内存使用量为代价的。 MapFile 依次遍历文件中所有条目的过程类似于 SequenceFile 中的过程:首先新建 MapFile. Reader() 实例,然后调用 next ()方法,直到返回值为 false ,该值表示没有条目返回,因为已经读到文件末尾。MapFile. Reader、首先将 index 文件读入内存(由于索引是缓存的,所以后续的随机访问将使用内存中的同一索引)。接着对内存中的索引进行二分查找,最后找到小于或等于搜索索引的键496 。在本例中,找到的键位 385,对应的值为 18030 ,该值为 data 文件中的偏移量,接着顺序读取 data 文件中的键,直到读取到 496 为止。至此,才找到键所对应的值,最后从 data 文件中读取相应的
值。整体而言,一次查找需要一次磁盘寻址和一次最多有 128 个条目的扫描。对于 随机访问而言,这是非常高效的。 getClost() 方陆与 get ()方法类似,不同的是前者返回与指定键匹配的最近的键,井不是在不匹配时返回 null 大型 MapFile 的索引会占据大量内存。可以不选择在修改索引间隔之后重建索引,而是在读取索引时设置 io.mao.index.skip 属性来加载一部分索引键。该属性通常设置为0 ,这意味着不跳过索引键 如果设置为 1,则表示每次跳过索引键中的一个(也就是索引键中的每隔一个键) ,也就是说,只读索引三分之二的键, 设置大的跳跃值可以 节省大量的内存,但会增加搜索时间,因为平均而言,扫描的键更多
 
MapFile 的变种:setFile,ArrayFile, BloomMapFile利用 布隆过滤器来实现了一个高性能的get()方法,对稀疏文件特别有用。
 
 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值