Hadoop技术内幕HDFS-笔记3之序列化

1.1.  序列化

org.apache.hadoop.io包

序列化:将一个对象编码为一个字节流

反序列化:相反过程

用途:

1、  作为一种持久化格式:可存储在硬盘上,供以后反序列化使用

2、  作为一种通信数据格式:可在JVM之间,通过网路相互传递

3、  复制的机制:深度复制

1.1.1. java内建序列化机制:

只有实现Serializabl接口(声明)即可。

对象输出流ObjectOutputStream 的writeObject(obj)方法

序列化输出的结果中包含了大量与类有关的信息。

对象输入流ObjectInputStream的readObject()方法,读取对象时必须小心跟踪存储对象的数量、顺序和类型

对于父类的处理,如果父类没有实现串行化接口,则其必须有默认的构造函数(即没有参数的构造函数)。否则编译的时候就会报错。在反串行化的时候,默认构造函数会被调用(即会调用构造方法创建新的对象)。但是若把父类标记为可以串行化,则在反串行化的时候,其默认构造函数不会被调用。这是为什么呢?这是因为Java 对串行化的对象进行反串行化的时候,直接从流里获取其对象数据来生成一个对象实例,而不是通过其构造函数来完成(即无新的对象进行构造)。

package com.test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerialDemo {

	public static void main(String[] args) throws Exception {
		//待写入的对象
		String obj1 = "Hello world!";
		
		FileOutputStream fos  = new FileOutputStream("test.out");
		ObjectOutputStream out = new ObjectOutputStream(fos);
		out.writeObject(obj1);
		out.writeInt(1024);
		out.close();
		fos.close();
		//顺序读取对象
		ObjectInputStream in = new ObjectInputStream(new FileInputStream("test.out"));
		String temp = (String) in.readObject();
		int temp2 = in.readInt();
		System.out.println(temp+temp2);
		in.close();
	}
}

 

java序列化输出保存了大量的附加信息,导致序列化结果膨胀,对于需要保存和处理大规模数据的hadoop来说,需要一个新的序列化机制。

java反序列化会不断的创建新的对象(hadoop的不会,而是实现了对对象的复用)

1.1.1. hadoop序列化

实现了Writable接口,write(out) 和readFileds(in)方法

实验:同时输出三个long类型的长度

package test;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectOutputStream;

import org.apache.hadoop.hdfs.protocol.Block;

public class SerialDemo {

	public static void main(String[] args) throws Exception {
		test1();
		test2();
	}
	private static void test2() throws IOException {
		//第一个参数为block的Id,第二个为分配的长度,第三个戳,共三个标志
			//此Block为自定义块,而不是使用hadoop自带的。可以看后面
		Block block1 = new Block(1L,24L,1L);
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		DataOutputStream  dout1 = new DataOutputStream(bout);
		//序列化对象到IO流中
		block1.write(dout1);
		System.out.println("hadoop序列化长度(三个long):"+bout.size());
	}

	private static void test1() throws FileNotFoundException, IOException,
			ClassNotFoundException {
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		ObjectOutputStream out = new ObjectOutputStream(bout);
		out.writeLong(1L);
		out.writeLong(1L);
		out.writeLong(1L);
		
		out.close();
		System.out.println("java序列化长度(三个long):"+bout.size());
	}
}

 

结果:

         java序列化长度(三个long):30

     hadoop序列化长度(三个long):24

1.1.1. hadoop序列化特征

1、  紧凑,带宽是hadoop集群中最稀缺的资源,一个紧凑的序列化机制可以充分利用数据中心的带宽

2、  快速:进程间通信,会大量使用序列化机制,因此,必须尽量减少序列化和反序列化的开销

3、  可扩展:升级

4、  互操作:不同语言之间的通信

引入了org.apache.hadoop.io.Writable接口,作为所有可序列化对象必须实现的接口

示例:

class Block implements Writable{
	private long blockId;
	private long numBytes;
	private long stamp;
	
	public Block(long blockId, long numBytes, long stamp) {
		super();
		this.blockId = blockId;
		this.numBytes = numBytes;
		this.stamp = stamp;
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		// TODO Auto-generated method stub
		blockId = in.readLong();
		numBytes = in.readLong();
		stamp = in.readLong();
	}

	@Override
	public void write(DataOutput out) throws IOException {
		// TODO Auto-generated method stub
		out.writeLong(blockId);
		out.writeLong(numBytes);
		out.writeLong(stamp);
	}
}

 

hadoop序列化的重要接口:

           WritableComparable 类型比较接口,用于key的自定义实现如IntWritable,LongWritable等java基本类型和Text等

           RawComparable(读取时) 实现高效比较能力,允许直接比较流中对象,省去创建对象的所有开销,选择时可以按需所取。

           public int compare (byte[] b1,int s1,int e1,byte[] b2,int s2,int e2);

           WritableComparator:实现了接口RawComparable,同时提供了一个获取一个Writable类的比较器

           com = WritableComparator.get(IntWritable.class);//获取IntWritable的比较器

           典型的Writable类:(short和char没有实现,可以使用IntWritable)

对于可变长度,因内部实现存在许多逻辑判断,不建议使用。

           对于ObjectWritable作为一种通用的标志,相当浪费资源,如果类型的数量不多,可以通过一个静态数组来提高效率,并使用数组的索引作为类型的序列化引用。不建议使用。实际上,对于自定义的类型已满足需求。

           另外还有一个GenericWritable类,用于对ObjectWritable进行替换,也不建议使用,内部逻辑判断很多。

1.1.1.   hadoop的序列化框架

除了Writable其它的序列化框架也能和Hadoop配合。(可在实际需要时采用)

1、Avro:动态语言友好,可以使用Python等实现Mapreduce

2、Thrift:跨语言的服务开发框架

3、Google Protocol Buffer 提供了轻便高效的结构化数据存储格式,支持C++,java,python三者语言的API。

hadoop通过一个简单的序列化API,集成各种序列化的实现。有org.apache.hadoop.io.serializer包里的Serialization实现

	private static void test3(){
		//对一个long类型进行序列化
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		SerializationUtils.serialize(1L, bout);
		
		System.out.println("hadoop Serialization序列化长度(1个long):"+bout.size());
		
		long temp = (Long) SerializationUtils.deserialize(SerializationUtils.serialize(1L));
		System.out.println(temp);
	}

 

结果:

hadoop Serialization序列化长度(1个long):82

1

长度竟然为82,所以不建议使用hadoop的Serialization

posted @ 2014-06-30 22:51  jseven  阅读(472)  评论(0编辑  收藏  举报