Hadoop 序列化
Hadoop序列化机制
序列化简介
- 序列化
- 序列化就是把内存中的对象,转换成字节序列(或其他数据传输协议)以便于存储(持久化)和网络传输。
- 反序列化
- 反序列化就是将收到字节序列(或其他数据传输协议)或者是硬盘的持久化数据,转换成内存中的对象。
Hadoop序列化机制的起源
Hadoop涉及到大量的数据(在Java
中就是对象)的传输(IO
),并且网络带宽稀缺,因此使用序列化机制迫不及待,序列化就是把内存中的对象转化成二进制流进行传输。数据序列化的要求有四个(紧凑,快速,可扩展,支持互操作),而Java
语言自带的序列化机制并不符合这些理想要求,因此专为Hadoop单独设计了一套序列化机制:Writable
。
Hadoop序列化和Java序列化的区别
Java
的序列化是一个重量级序列化框架(Serializable
),- 一个对象被序列化后,会附带很多额外的信息(各种校验信息,
header
,继承体系。), - 不便于在网络中高效传输;
Hadoop的序列化机制Writable
具有精简高效的特点。
Hadoop序列化
Hadoop在集群之间进行通讯或者RPC调用的时候,需要序列化,而且要求序列化要快,且体积要小,占用带宽要小。
序列化和反序列化在分布式数据处理领域经常出现:进程通信和永久存储。Hadoop中各个节点的通信是通过远程调用(RPC)实现的,RPC序列化具有以下特点:
- 紧凑:紧凑的格式能让我们充分利用网络带宽,而带宽是数据中心最稀缺的资;
- 快速:进程通信形成了分布式系统的骨架,所以需要尽量减少序列化和反序列化的性能开销,这是基本的;
- 可扩展:协议为了满足新的需求变化,所以控制客户端和服务器过程中,需要直接引进相应的协议,这些是新协议,原序列化方式能支持新的协议报文;
- 互操作:能支持不同语言写的客户端和服务端进行交互;
常用数据序列化类型
常用的数据类型对应的hadoop数据序列化类型
Java类型 | Hadoop Writable类型 | 释义 |
---|---|---|
boolean | BooleanWritable | 标准布尔型数值 |
byte | ByteWritable | 单字节数值 |
int | IntWritable | 整型数值 |
float | FloatWritable | 单精度数 |
long | LongWritable | 长整型数值 |
double | DoubleWritable | 双精度数 |
String | Text | 使用UTF8格式存储的文本 |
map | MapWritable | 以键值对的形式存储Writable类型的数据 |
array | ArrayWritable | 以数组的形式存储Writable类型的数据 |
null | NullWritable | 当<key,value>中的key或value为空时使用 |
Writable
序列化接口
Writable
的序列化和反序列化
write()
是把每个对象序列化到输出流———Writable序列化readFields()
是把输入流字节反序列化———-Writable反序列化
Writable
之NullWritable
NullWritable
是Writable
的一个特殊类型,它的序列化长度为0。它并不从数据流中读取数据,也不写入数据。它充当占位符;例如,在MapReduce
中,如果不需要使用键或值,就可以将键或值声明为NullWritable
————结果是存储常量空值。如果希望存储一系列数值,与键/值对相对,NullWritable
也可以用作在SequenceFile
中的键。它是一个不可变的单实例类型:通过调用NullWritable.get()
方法可以获取这个实例。
WritableComparable
排序接口
WritableComparator
是对继承自WritableComparable
类的RawComparator
类的一个通 用实现。它提供两个主要功能。第一,它提供了对原始 compare()
方 法的一个默认实现,该方法能够反序列化将在流中进行比较的对象,并调用对象的compare()
方法。第二,它充当的是RawComparator
实例的工厂(已注册Writable的实现)。例如,为了获得IntWratable
的comparator
, 我们直接如下调用:
RawComparator<IntWritable> comparator = WritableComparator.get(Intwritable.class);
这个comparator
可以用于比较两个IntWritable
对象:
IntWritable w1 = new IntWritable(163);
IntWritable W2 = new IntWritable(67);
assertThat( comparator . compare(W1, W2), greaterThan(0));
或其序列化表示:
byte[] b1 = serialize(w1);
byte[] b2 = serialize(W2);
assertThat(comparator. compare(b1, B, b1.1ength, b2, 0, b2.1ength),
greaterThan(0));
对MapReduce
来说,类型的比较是非常重要的,因为中间有个基于键的排序阶段。Hadoop
提供的一个优化接口是继承自Java Comparator
的RawComparator
接口:
package org. apache . hadoop.io;
import java . ut il . Comparator;
public interface RawComparator<T> extends Comparator<T> {
public int compare(byte[] b1, int S1, int 11, byte[] b2,int s2,int 12);
}
该接口允许其实现直接比较数据流中的记录,无须先把数据流反序列化为对象,这样便避免了新建对象的额外开销。例如,使用IntWritable
接口实现的comparator
实现了compare()
方法,该方法可以从每个字节数组bl和b2中读取给定起始位置(s1和s2)以及长度(l1和I2)的一个整数进而直接进行比较。
自定义对象实现MR的序列化接口
使用自定义类型的原因
自带的类实现了对整形,浮点型,布尔型及String
和Text
类)的封装,都是比较简单的数据类型,在实际应用中,对于一些属于用户自己的功能,系统无法预知,通常需要自定义数据类型。
自定义类型
如果需要将自定义的bean放在key中传输,则还需要实现comparable
接口,因为Mapreduce
框中的shuffle
过程一定会对key
进行排序,自定义的bean实现的接口:
public class Cat implements WritableComparable<Cat>
- 重写
readFields(DataInput in)
方法,实现反序列化 - 重写
write(DataOutput out)
方法,实现序列化 - 重写
compareTo(Cat o)
方法,实现排序- compareTo() 方法返回值
- 如果 this的值等于传入的参数 返回 0
- 如果this的值小于传入的参数 返回 负数
- 如果this的值大于传入的参数 返回 正数
- compareTo() 方法返回值
案例
package hadoop.mr.custom.GroupingComparator;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* 订单bean
*/
public class OrderBean implements WritableComparable<OrderBean>{
private String orderId;
private String productId;
private Double amount;
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
/**
* 现在需要求出每一个订单中成交金额最大的一笔交易
* 订单id视为相同订单
* 相同订单按金额降序排序
*
* compareTo() 方法返回值
* 如果 this的值等于传入的参数 返回 0
* 如果this的值小于传入的参数 返回 负数
* 如果this的值大于传入的参数 返回 正数
* @param o
* @return
*/
@Override
public int compareTo(OrderBean o) {
int cmp = this.orderId.compareTo(o.getOrderId());
if (cmp==0){
//降序排序
cmp= -this.amount.compareTo(o.getAmount());
}
return cmp;
}
@Override
public void write(DataOutput out) throws IOException {
out.writeUTF(orderId);
out.writeUTF(productId);
out.writeDouble(amount);
}
@Override
public void readFields(DataInput in) throws IOException {
orderId=in.readUTF();
productId=in.readUTF();
amount=in.readDouble();
}
@Override
public String toString() {
return "orderId='" + orderId + '\'' +
", productId='" + productId + '\'' +
", amount=" + amount ;
}
}