对象的序列化用于将对象编码成一个字节流,以及从字节流中重新构建对象。
将一个对象编码成一个字节流叫做序列化改对象(Serializing)
相反的处理过程称为反序列化(Deserializing)
序列化的主要用途:
- 作为一种持久化格式
一个对象被序列化之后,它的编码可以被存储到磁盘上,供反序列化使用 - 作为一种通信数据格式
序列化的结果可以从一个正在运行的虚拟机,通过网络传输到一个虚拟机上 - 作为一种拷贝,克隆机制
将对象序列化到内存,然后通过反序列化,可以得到一个对已经存在的对象的深拷贝的新对象
在hadoop中,序列化是要使用的是数据持久化和通信数据格式两种功能。
JDK中自带序列化
JDK的序列化只有实现了serializable接口就能实现序列化与反序列化,但是记得一定要加上序列化版本ID serialVersionUID,这个是用来识别序列化的之前那个类的到底是哪一个。
我们显式设置这个序列化版本ID的目的就是为了:
- 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
- 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
java序列化算法要点:
- 将对象实例相关的元数据输出
- 递归的输出类的超类描述直到不再有超类
- 类元数据完了之后,开始从最顶层的超类开始输出对象的实例的实际数据值
- 从上到下递归输出实例的数据
java序列化与反序列化实现方法
- 创建一个对象,并实现Serializbale接口
- 序列化
ObjectOutputStream.writeObject(obj);
反序列化ObjectOutputStream.readObject();
java序列化不足之处:
java序列化将每个对象的类名写入到输出流中,这就导致了java序列化对象需要占用比原对象更多的存储空间。
java的反序列化会不断的创建对象,这会给系统带来一定的开销。
正是由于java序列化的不足,所以hadoop没有直接使用java序列化,而是实现了自己的序列化机制,在hadoop序列化中,用户可以复用对象,这样就减少了java对象的分配和回收,提高了应用的效率。
Hadoop序列化特点:
- 紧凑
由于带宽是hadoop集群中稀缺的资源,一个紧凑的序列化机制可以充分利用数据中心的带宽 - 快速
在进程间通信时会大量使用序列化机制,因此必须尽量减少序列化和反序列化的开销 - 可扩展
随着系统的发展,系统间通信的协议升级,类的定义会发生变化,序列化机制需要支持这些升级和变化 - 互操作
可以支持不同开发语言间的通信,如c++和java之间的通信。
Hadoop序列化
hadoop中通过实现Writable接口来实现序列化,它比较紧凑、快速。
1 | public interface Writable { |
write方法用于将对象写入二进制的DataOutput中,完成序列化
readFields从DataInput流中读取数据,完成反序列化
下面是一个demo:
1 | public class MyWritable implements Writable { |
hadoop中常用的序列化文件如下图所示
BytesWritable
BytesWritable类型是一个二进制数组的封装类型,序列化格式是以一个4字节的整数。
NullWritable
NullWritable是一个非常特殊的Writable类型,序列化不包含任何字符,仅仅相当于个占位符。在MapReduce编程中,key或者value在不需要使用时,可以定义为NullWritable。
ObjectWritable
ObjectWritable是其他类型的封装类,包括java原生类型,String,enum,null等,或者这些类型的数组。当一个field有多种类型时,就可以使用ObjectWritable,不过有个不好的地方就是占用的空间太大,即使你存一个字母,因为它需要保存封装前的类型。
GenericWritable
使用GenericWritable时,只需继承他,并通过getTypes方法指定哪些类型需要支持即可。
GenericWritable的序列化只是把类型在type数组里的索引放在了前面,这样就比ObjectWritable节省了很多空间,所以推荐大家使用GenericWritable
集合类型的Writable
ArrayWritable和TwoDArrayWritable
ArrayWritable和TwoDArrayWritable分别表示数组和二维数组的Writable类型,指定数组的类型有两种方法,构造方法里设置,或者继承于ArrayWritable,TwoDArrayWritable也是一样。
MapWritable和SortedMapWritable
MapWritable对应Map,SortedMapWritable对应SortedMap,以4个字节开头,存储集合大小,然后每个元素以一个字节开头存储类型的索引(类似GenericWritable,所以总共的类型总数只能倒127),接着是元素本身,先key后value,这样一对对排开。
这两个Writable以后会用很多,贯穿整个hadoop,这里就不写示例了。
hadoop序列化中没有set集合和list集合,但是可以代替实现。用MapWritable代替set,SortedMapWritable代替sortedmap,只需将他们的values设置成NullWritable即可,NullWritable不占空间。可以用ArrayWritable代替list集合,不同类型的list可以用GenericWritable实现类型,然后再使用ArrayWritable封装。当然MapWritable一样可以实现list,把key设置为索引,values做list里的元素。