hadoop文件的序列化

举报
Smy1121 发表于 2019/06/20 14:26:02 2019/06/20
【摘要】 一般来说,“活的”对象只生存在内存里,关机断电就没有了。而且“活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机。 然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。

为什么要序列化?

        一般来说,“活的”对象只生存在内存里,关机断电就没有了。而且“活的”对象只能由本地的进程使用,不能被发送到网络上的另外一台计算机。 然而序列化可以存储“活的”对象,可以将“活的”对象发送到远程计算机。


什么是序列化?

        序列化就是指将结构化对象 (实例) 转化为字节流 (字符数组)。反序列化就是将字节流转向结构化对象的逆过程。 于是,如果想把“活的”对象存储到文件,存储这串字节即可,如果想把“活的”对象发送到远程主机,发送这串字节即可,需要对象的时候,做一下反序列化,就能将对象“复活”了。 

        将对象序列化存储到文件,术语又叫“持久化”。将对象序列化发送到远程计算机,术语又叫“数据通信”。


为什么不用Java的序列化?

        Java的序列化机制的缺点就是计算量开销大,且序列化的结果体积大太,有时能达到对象大小的数倍乃至十倍。它的引用机制也会导致大文件不能分割的问题。这些缺点使得Java的序列化机制对Hadoop来说是不合适的。于是Hadoop设计了自己的序列化机制。


   

为什么序列化对Hadoop很重要?

         因为Hadoop在集群之间进行通讯或者RPC调用的时候,需要序列化,而且要求序列化要快,且体积要小,占用带宽要小。所以必须理解Hadoop的序列化机制。


        序列化和反序列化在分布式数据处理领域经常出现:进程通信和永久存储。然而Hadoop中各个节点的通信是通过远程调用(RPC)实现的,那么 RPC序列化要求具有以下特点:


1. 紧凑:紧凑的格式能让我们能充分利用网络带宽,而带宽是数据中心最稀缺的资源;

2. 快速:进程通信形成了分布式系统的骨架,所以需要尽量减少序列化和反序列化的性能开销,这是基本的;

3. 可扩展:协议为了满足新的需求变化,所以控制客户端和服务器过程中,需要直接引进相应的协议,这些是新协议,原序列化方式能支持新的协议报文;

4. 互操作:能支持不同语言写的客户端和服务端进行交互; 

Hadoop中定义哪些序列化相关的接口呢?


        Hadoop中定义了两个序列化相关的接口:Writable 接口和 Comparable 接口,这两个接口可以合并成一个接口 WritableComparable。下面我们就了解一下这两个序列化接口:


        Writable接口


        所有实现了Writable接口的类都可以被序列化和反序列化。 Writable 接口中定义了两个方法,分别为write(DataOutput out)和readFields(DataInput in)。write 用于将对象状态写入二进制格式的DataOutput流,readFields 用于从二进制格式的 DataInput 流中读取对象状态。


package org.apache.hadoop.io;      

import java.io.DataOutput;  

import java.io.DataInput;  

import java.io.IOException;      

public interface Writable {  

/** 

* 将对象转换为字节流并写入到输出流out中 

*/  

void write(DataOutput out) throws IOException;  

/** 

* 从输入流in中读取字节流反序列化为对象 

*/  

void readFields(DataInput in) throws IOException;  

}

        对于一个特定的 Writable,我们可以对它进行哪些操作呢?有两种常用操作:赋值和取值,这里我们以 IntWritable 为例来分别说明(IntWritable 是对 Java 的 int 类型的封装)。


        1、通过 set() 函数设置 IntWritable 的值。


IntWritable value = new IntWritable();

value.set(588)

        类似的,也可以使用构造函数来赋值。


IntWritable value = new IntWritable(588);

        2、通过get() 函数获取 IntWritable 的值。


int result = value.get();//这里获取的值为588

        Comparable接口


        所有实现了Comparable的对象都可以和自身相同类型的对象比较大小。该接口定义为:


package java.lang;  

import java.util.*;      

public interface Comparable {  

/** 

* 将this对象和对象o进行比较,约定:返回负数为小于,零为大于,整数为大于 

*/  

public int compareTo(T o);  

}

Hadoop 需要自定义 Writable 接口

        虽然 Hadoop 自带一系列Writable实现,如IntWritable,LongWritable等,可以满足一些简单的数据类型。但有时,复杂的数据类型需要自己自定义实现。通过自定义Writable,能够完全控制二进制表示和排序顺序。


        Writable 是 MapReduce 数据路径的核心,所以调整二进制表示对其性能有显著影响。现有的 Hadoop Writable 应用已得到很好的优化, 但为了对付更复杂的结构,最好创建一个新的 Writable 类型,而不是使用已有的类型。下面我们来学习一下如何自定义 Writable 类型。


自定义一个 Writable 类型 TextPair


        为了演示如何创建一个自定义 Writable ,编写一个一对字符串的实现,对象名称为 TextPair,代码如下所示。


importjava.io.*;

importorg.apache.hadoop.io.*;

public class TextPair implements WritableComparable {

private Text first;//Text 类型的实例变量 first

private Text second;//Text 类型的实例变量 second

public TextPair() {

set(newText(),newText());

}

public TextPair(String first, String second) {

set(new Text(first),new Text(second));

}

public TextPair(Text first, Text second) {

set(first, second);

}

public void set(Text first, Text second) {

this.first = first;

this.second = second;

}

public Text getFirst() {

return first;

}

public Text getSecond() {

return second;

}

//将对象转换为字节流并写入到输出流out中

@Override

public void write(DataOutput out)throwsIOException {

first.write(out);

second.write(out);

}

//从输入流in中读取字节流反序列化为对象

@Override

public void readFields(DataInput in)throwsIOException {

first.readFields(in);

second.readFields(in);

}

@Override

public int hashCode() {

return first.hashCode() *163+ second.hashCode();

}

@Override

public boolean equals(Object o) {

if(o instance of TextPair) {

TextPair tp = (TextPair) o;

return first.equals(tp.first) && second.equals(tp.second);

}

return false;

}

@Override

publicString toString() {

return first +"\t"+ second;

}

//排序

@Override

public int compareTo(TextPair tp) {

int cmp = first.compareTo(tp.first); 

if(cmp !=0) {

return cmp;

}

return second.compareTo(tp.second);

}

}

        TextPair对象有两个Text实例变量(first和second)和相关的构造函数、get方法和set方法。 所有的Writable实现都必须有一个默认的构造函数,以便MapReduce框架能够对它们进行实例化,进而调用readFields()方法来填充它们的字段。Writable 实例 是易变的、经常重用的,所以应该尽量避免在 write() 或 readFields() 方法中分配对象。


        通过委托给每个 Text 对象本身,TextPair 的 write() 方法依次序列化输出流中的每一个 Text 对象。同样也通过委托给 Text 对象本身,readFields() 反序列化 输入流中的字节。DataOutput 和 DataInput 接口有丰富的整套方法用于序列化和反序列化 Java 基本类型,所以在一般情况下,能够完全控制 Writable 对象的数据传输格式。


        正如为Java写的任意值对象一样,会重写java.lang.Object的hashCode()、equals()和toString()方法。 HashPartitioner使用hashcode()方法来选择reduce分区,所以应该确保写一个好的哈希函数来确定reduce函数的分区在大小上是相当的。


        TextPair 是WritableComparable的实现,所以它提供了 compareTo()方法的实现,加入我们希望的排序:通过一个一个String逐个排序。


【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。