第四章之Hadoop I/O
数据的完整性
检测数据是否损坏的常见措施是:在数据第一次引入系统的时候计算校验和(checksum),并在数据通过一个不可靠的通道进行传输时候再次计算校验和,这样就能发现数据是否损坏。如果新的校验和和原来的校验和不匹配,我们就认为数据已经损坏。常用的数据检测码是:CRC-32(循环冗余校验)
HDFS的数据完整性
datanode负责验证收到的数据后存储数据及其校验和,它在收到客户端的数据或复制期间其他datanode的数据时候执行这个操作。正在写数据的客户端将数据极其校验和发送到由一些列datanode组成的管线,管线中的最后一个datanode负责验证校验和。如果datanode检测到错误,客户端变收到一个ChecksumException异常。
客户端从datanode读取数据的时候,也会验证校验和,将他们与datanode中存储的校验和进行比较。每个datanode均持久保存有一个用户验证的校验和日志(persistent log of checksum verification),so他知道每个数据块最后一次的验证时间。客户端成功验证一个数据块以后,会告诉这个datanode,datanode由此更新日志。不只是客户端在读取数据的时候会验证校验和,每个datanode也会在一个后台线程中运行DataBlockScanner,从而定期验证存储在这个datanode上的所有数据块。
LocalFileSystem
Hadoop的LocalFileSystem执行客户端的校验和验证。LocalFileSystem通过ChecksumFileSystem来完成自己的任务,有了这个类,向其他文件系统加入校验和就非常简单。
压缩
文件压缩有两大好处:可以减少存储文件所需要的磁盘空间,可以加速数据在网络和磁盘上的传输。需要处理大量数据的时候,这两大好处是相当重要的。
gzip是一个通用的压缩工具。
codec实现了一种压缩--解压缩算法。
通过CompressionCodec对数据流进行压缩和解压缩
压缩和输入分片
在考虑如何压缩将由MapReduce处理的数据的时候,理解这些压缩格式是否支持切分(splitting)是非常重要的。gzip并不支持文件切分。
在MapReduce中使用压缩
如果输入的文件是压缩的,那么在根据文件扩展名推断出相应的codec后,MapReduce会在读取文件时候自动解压缩文件。
想要对MapReduce作业的输出进行压缩操作,应在配置过程中,把mapred.output.compress属性设置为true和mapred.output.compression.codec
对Map任务输出进行压缩
尽管MapReduce应用读写的是未经过压缩的数据,但是如果对map阶段的中间输入进行压缩,也可以获得不少好处。由于map任务的输出需要写到磁盘并通过网络传输到reducer节点,所以如果使用LZO这样的快速压缩方式,是可以获得性能提升的,因为需要传输的数据减少了。
序列化
所谓序列化(serialization)是指将结构化对象转化为字节流,以便在网络上传输或者写到磁盘进行永久存储。
反序列化(deserialization)是指讲字节流转回结构化对象的逆过程。
序列化在分布式数据处理的两大领域经常出现:进程间通信和永久存储。
在Hadoop中,系统中多个节点上进程间的通信是通过“远程过程调用RPC”实现的,RPC协议将消息序列成二进制流之后发送到远程节点,远程节点接着将二进制流反序列化为原始信息。
Hadoop使用自己的序列化格式:Writable,它格式紧凑,速度快,但很难用java以外的语言进行扩展和使用。
Writable接口:
writable接口定义了两个方法:一个讲其状态写到DataOutput二进制流,另一个从DataInput二进制流读取其状态。
Writable集合类
在org.apache.hadoop.io包中,有4个Writable集合类:ArriyWritable,TwoDArrayWritable,MapWritable,SortedMapWritable
序列化框架
尽管大多数MapReduce程序使用的都是Writable类型的键和值,但这并不是MapReduce强制使用的,事实上可以使用任何类型,只要有一中机制能对每个类型进行类型与二进制的表示的来回切换。
为了支持这一个机制,Hadoop有一个针对可替换序列框架(serialization framework)的API
Avro
Apache Avro 是一个独立于编程语言的数据序列化系统,该项目旨在解决Hadoop中Writable类型的不足:缺乏语言的可移植性。
为什么要有一个新的数据序列化系统?与Apache Thrift和Google 的 Protocol Buffers相比,Avro有独特的特性,
Avro数据是用语言无关的模式定义的,但是与其他系统不同的是,Avro的代码生成是可选的,这意味着你可以对遵循指定模式的数据进行读写操作,即使再次之前代码,从来没有见过这个特殊的数据模式,为此,Avro假设数据模式总是存在的。Avro模式通常使用JSON编写,而数据通常用二进制格式编码,但也有其他选择。
基于文件的数据结构
对于某些应用而言,需要特殊的数据结构来存储自己的数据,对于基于MapReduce的数据处理,讲每个二进制数据的大对象(blob)融入自己的文件中并不能实现很高的扩展性,所以针对上述情况Hadpp开发了一组更高层次的容器。
SequenceFile
考虑到日志文件,其中每一条日志记录是一行文本,如果想记录二进制类型,纯文本是不合适的。这种情况下,Hadoop的SequenceFile类非常合适,因为上述类提供了二进制键/值对的永久存储的数据结构。当作为日志文件的存储格式的时候,你可以自己选择键,比如由LongWritable类型表示的时间戳,以及值可以是Writable类型,用于表示日志记录的数量。
SequenceFiles同样可以作为小文件的容器,而HDFS和MapReduce是针对大文件进行优化的,所以通过SequenceFile类型将小文件包装起来,可以获得更高效率的存储和处理。
SequenceFile的写操作,通过createwriteer()静态方法可以创建SequenceFile对象,并返回SequenceFile.Writer实例。存储在SequenceFile中的键和值并不一定需要是Writable类型,任一可以通过Serialization类实现序列化和反序列化的类型均可以被使用。一旦拥有SequenceFile.Writer实例,就可以通过append()方法在文件末尾附加键/值对。写完之后,可以调用close()方法。
SequenceFile读取操作。从头到尾读取顺序文件的过程是创建SequenceFile.Reader实例后反复调用next()方法迭代读取记录的过程。读取的是哪条记录与你使用的序列化框架相关。如果是Writable类型,那么通过键/值作为参数的next()方法可以将数据流中的下一条键值对读入变量中。
排序和合并顺序文件
MapReduce是对多个顺序文件进行排序(或者合并)最有效的方法,MapReduce本身具有并行执行能力,并且可以由你指定reducer的数量(该数决定着输出分区数),例如,通过指定一个reducer,可以得到一个输出文件。通过MapReduce实现排序/归并的另一种方法是是用SequenceFile.Sorter类中的sort()方法和merge()方法。
顺序文件的格式
顺序文件由文件头和最后的一条或者多条记录组成,顺序文件的前三个字节为SEQ,紧随其后的一个字节表示顺序文件的版本号。文件头还包括其他一些字段,包括键和值相应类的名称,数据压缩细节,用户定义的元数据,以及同步标志。
MapFile
MapFile是已经排序的SequenceFile,他已经加入用于搜索键的索引。
写入MapFile 类似与SequenceFile的写入。读取也是一样类似。在MapFile中搜索就相当于在索引和排过序的SequenceFile中搜索,所以可以把Sequence转换为MapFile。