Hadoop I/O系统介绍

jopen 12年前

    看过很多Hadoop介绍或者是学习的帖子和文章,发现介绍Hadoop I/O系统的很少。很多文章都会介绍HDFS的架构和使用,还有MapReduce编程等等。尤其是在介绍Hadoop的MapReduce编程之前,首 先必须了解下Hadoop的I/O知识,要不一看到IntWritable、LongWritable、Text、NullWritable等概念就有点 犯晕,看到和普通的Java程序类似的MapReduce程序就觉得很难。如果这时候你知道其实IntWritable就是其他语言如Java、C++里 的int类型,LongWritable就是其他语言里的long,Text类似String,NullWritable就是Null,这样你就会很轻易 的明白Hadoop 的MapReduce 程序。就像在学习其他编程语言之前必须先学习数据类型一样,在学习Hadoop的MapReduce编程之前,最好先学习下Hadoop的I/O知识。这 里就简要介绍Hadoop的I/O知识,就当抛砖引玉吧。


    1序列化和反序列化
     序列化(Serialization)就是结构化的对象转化为字节流,这样可以方便在网络上传输和写入磁盘进行永久存储(原因看完这部分后就明白了)。反序列化(deserialization)就是指将字节流转回结构化对象的逆过程。
    序列化和反序列化在分布式数据处理里主要出现在进程间通行和永久存储两个应用领域。在Hadoop 系统中,系统中多个节点上的进程间通信是通过远程过程调用(romote procedure call,R PC)实现的,RPC协议将消息序列转化为二进制流后发送到远程节点,远程节点接着将二进制流反序列化为消息,所以RPC对于序列化有以下要求(也就是进 程间通信对于序列化的要求):
    (1)紧凑,紧凑的格式可以提高传输效率,充分利用网络带宽,要知道网络带宽是数据中心的一种非常重要的资源。
    (2)快速,进程间通信是分布是系统的重要内容,所以必须减少序列化和反序列化的开销,这样可以提高整个分布式系统的性能。
    (3)可扩展,通信协议为了满足一些新的需求,比如在方法调用的过程中增加新的参数,或者新的服务器系统要能够接受老客户端的旧的格式的消息,这样就需要直接引进新的协议,序列化必须满足可扩展的要求。
    (4)互操作,可以支持不同语言写的客户端(比如C++、java、Python等等)与服务器交互。
     前面说了序列化的目的是可以方便在网络上传输和写入磁盘进行永久存储,前面讲了进程间通信对于序列化的要求,下面来说一说数据永久存储对于序列化的要 求。前面说的4个序列化的要求也是数据永久存储所要求的,存贮格式紧凑可以高效的利用存储空间,快速可以减少读写数据的额外开销,可扩张这样就可以方便的 读取老格式的数据,互操作就可以满足不同的编程序言来读写永久存贮的数据。


    2 Hadoop的序列化格式——Writabale
    前面介绍了序列化对于分布式系统是至关重要的,Hadoop 定义了自己的序列化格式Writable,它是通过Java语言实现的,格式紧凑速度快。Writable是Hadoop的核心,很多MapReduce程序都会为键和值使用它。
    2.1Writabale接口
    Writable接口定义了两个方法,一个将其状态写到DataOutput二进制流(往二进制流里面写数据),一个从DataInput二进制流读取其状态(从二进制流里读取数据)。
    2.2Writable类

    Hadoop自带的org.apache.hadoop.io包含有广泛的Writable类可供选择。下面给出Writable类的层次结构。

         Hadoop I/O系统介绍

    下面介绍几个常用的Writable类:
    (1)Text类:
        Text类是针对UTF-8序列(UTF-8是UNICODE的一种变长字节字符编码,又称万国码)的Writable类,一般认为他等价与 java.lang.String的Writable类。但是他和String还是有一些差异的。Text的索引是按编码后字节序列中的位置来实现 的,String是按其所包含的char编码单元来索引的。看下面的例子:
            String s=String("\u0041\u00df\uu6671\ud801\uDc00");
            Text t=Text("\u0041\u00df\uu6671\ud801\uDc00");
            s.indexof("\u0041")==0            t.find("\u0041")==0
            s.indexof("\u00df")==1             t.find("\u00df")==1
            s.indexof("\u6671")==2            t.find("\u6671")==3
            s.indexof("\ud801\uDc00")==3 t.find(\ud801\uDc00")==6
            s.length()==5    s.getBytes("UTF-8").length()==10
            t.getLength()==10(1+2+3+4)
        通过字节偏移量来进行位置索引,实现对Text类的Unicode字符迭代是非常复杂的,因为不能简单的通过增加位置的索引值来实现。所以必先将Text 对象转化为java.nio.BytesBuffer对象,然后利用缓冲区对Text对象反复调用bytesToCodePoint()静态方法,该方法 能获取下一代码的位置。
        由于Text类不像java.lang.String类那样有丰富的字符串操作API,所以在一些情况下为了方便处理,需要将Text类转化为String类,这一过程通过toString来实现。
    (2)ByteWritable类,二进制数据数组的封装,它的序列化格式为一个用于指定后面数据字节数的整数域(4字节),后跟字节本身。
    (3)NullWritabe,是Writable的一个特殊类型,它的序列化长度为0,类似于null。
    (4)ObjectWritable类,是对java基本类型(String,enum,Writable,null或这些类型组成的数组)的一个通用封装。
    (5)Writable集合类,共有4个集合类,其中ArrayWritable是对Writable的数组的实现,TwoDArrayWritable 是对Writable的二维数组的实现,MapWritable是对Map的实现,SortedMapWritable是对SortedMap的实现。
    (6)定制的Writable类,我们可以根据自己的需求构造自己的Writable类,可以根据需要控制二进制表示和排序顺序,由于Writable是MapReduce数据路径的核心,所以调整二进制表示能对性能残生显著效果。


    3 Hadoop的序列化框架——Avro
    Avro(读音类似于[ævrə])是Hadoop的一个子项目,由Hadoop的创始人Doug Cutting(也是Lucene,Nutch等项目的创始人,膜拜)牵头开发,当前最新版本1.3.3。Avro是一个数据序列化系统,设计用于支持大 批量数据交换的应用。它的主要特点有:支持二进制序列化方式,可以便捷,快速地处理大量数据;动态语言友好,Avro提供的机制使动态语言可以方便地处理 Avro数据。 
     当前市场上有很多类似的序列化系统,如Google的Protocol Buffers, 非死book的Thrift。这些系统反响良好,完全可以满足普通应用的需求。针对重复开发的疑惑,Doug Cutting撰文解释道:Hadoop现存的RPC系统遇到一些问题,如性能瓶颈(当前采用IPC系统,它使用Java自带的 DataOutputStream和DataInputStream);需要服务器端和客户端必须运行相同版本的Hadoop;只能使用Java开发等。 但现存的这些序列化系统自身也有毛病,以Protocol Buffers为例,它需要用户先定义数据结构,然后根据这个数据结构生成代码,再组装数据。如果需要操作多个数据源的数据集,那么需要定义多套数据结构 并重复执行多次上面的流程,这样就不能对任意数据集做统一处理。其次,对于Hadoop中Hive和Pig这样的脚本系统来说,使用代码生成是不合理的。 并且Protocol Buffers在序列化时考虑到数据定义与数据可能不完全匹配,在数据中添加注解,这会让数据变得庞大并拖慢处理速度。其它序列化系统有如 Protocol Buffers类似的问题。所以为了Hadoop的前途考虑,Doug Cutting主导开发一套全新的序列化系统,这就是Avro,于09年加入Hadoop项目族中。
      Avro是Hadoop中的一个重要的项目,不是三言两语能说清楚的,这片文章是专门介绍Avro的,感觉写的不错,我前面写的这段来自于这篇文章,地址:[http://langyu.iteye.com/blog/708568]


    4 SequenceFile和MapFile
    Hadoop的HDFS和MapReduce子框架主要是针对大数据文件来设计的,在小文件的处理上不但效率低下,而且十分消耗内存资源(每一个小文件占 用一个Block,每一个block的元数据都存储在namenode的内存里)。解决办法通常是选择一个容器,将这些小文件组织起来统一存储。HDFS 提供了两种类型的容器,分别是SequenceFile和MapFile。
    这一篇博文是专门介绍SequenceFile和MapFile的,写的不错,想详细理解的可以查看,文章地址:http://blog.csdn.net/javaman_chen/article/details/7241087。


    关于Hadoop I/O还有数据的完整性和数据压缩的知识,由于篇幅原因,这里不再一一介绍。
    来自:http://my.oschina.net/u/872123/blog/120001