一、数据完整性
二、压缩
三、序列化
- 基本概念
- 序列化指的是将结构化对象转化为字节流以便于通过网络进行传输或写入持久化存储的过程
- 反序列化指的是将字节流转为一系列结构化对象的过程。
- 进程间通信和持久存储--分布式数据处理过程,出现序列化和反序列两个动作
- RPC是hadoop用于节点之间的进程间的通信,RPC协议使用序列化将消息编码转为二进制流,将其发送到远程节点,此后,二进制流被反序列化为原始消息。RPC序列化格式特点:紧凑(网络带宽充分利用)、快速(序列化和反序列化开销尽可能小)、可扩展性(协议随时间可变)、互操作性(不同语言)
- 数据持久性存储的特点:紧凑(高效使用存储空间)、快速(读取或写入TB级数据的开销变得极小)、可扩展(可以使用透明的方式读取用旧格式写入的数据)、互操作性(使用不同语言读取或写入持久化数据)
- Hadoop的序列化格式为Writables,MapReduce程序使用它来序列化键值对
- Writable接口
- 接口定义
public interface Writable {
void write(DataOutput out) throw IOException;//用于将状态写入二进制格式的DataOutput流中
void readFields(DataInput in) throw IOException;//用于从二进制格式的DataInput流中读取状态
}
-
类型的比较对MapReduce至关重要,键与键之间的比较是在排序阶段完成的,Hadoop提供了一个从Java Comparator接口优化的方法,RawComparator接口,该接口允许执行者比较从流中读取的未被反序列化为对象的记录,从而省去了创建对象的所有开销。
package org.apache.hadoop.io; import java.util.Comparator;
public interface RawComparator<T> extends Comparator<T>{
public int compare(byte [] b1,int s1,int l1,btye []b2,int s2,int l2);
} -
WritableComparator是RawComparator对WritableComparable类的一个通用实现。它提供了一个默认的对原始函数compare()函数的调用,对从数据流对要比较的对象进行反序列化,然后调用对象的compare方法。另外一个功能就是RawComparator实例的一个工厂方法。
RawComparator<IntWritable> comparator = WritableComparator.get(IntWritable.class);
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,0,b1.length,b2,0,b2.length),greaterThan(0));
- Writable类
- org.apache.hadoop.io
- Writable类层次图
- Writable基本类型的封装
- Writable的Text类:Text类是一种UTF-8格式的Writable。可以理解为一种与java.lang.String相似的Writable。Text类代替了UTF8类。UTF8类编码不支持编码大于32767个字节的字符,使用了Java的改进过的UTF-8。而Text类使用的是标准的UTF-8,使其更易于与理解的UTF-8的其他工具协同工作,故String与Text之间存在差异。Text类使用整型(使用一个可变长度的编码方案)在字符串编码中存储字节数,最大值为2GB。String类与Text类的区别如下:
- 索引
- Unicode
- 遍历(迭代):将Tet对象变成java.nio.ByteBuffer,然后对缓冲区的Text反复调用bytesToCodePoint()静态方法,这个方法提取下一个代码点作为int然后更新缓冲中的位置,当bytesToCodePoint()返回-1时,检测到字符串结束。
public class TextIterator {
public static void main(String[] arg){
Text t = new Text("\u0041\u00DF\u6771\uD801\uDC00"); ByteBuffer buf = ByteBuffer.wrap(t.getBytes(),0,t.getLength());
int cp;
while(buf.hashRemaining()&&(cp=
Text.bytesToCodePoint(buf)) != -1){
System.out.println(Integer.toHexString(cp));
} }
} -
可修改性,通过set()方法改变
Text t = new Text("hadoop");
t.set(new Text("pig")); -
Text类常用ToString方法转化为String类,因为String类具有丰富的API
Text t = new Text("hadoop");
String s = t.toString();
- ByteWritable类是一个二进制数据数组封装。它将序列化格式是一个int字段,指定的是字节数及其字节本身。
- NullWritable类是一种特殊的Writable类型,因为它的序列化是零长度的。没有字节被写入流或流中读取,它常被作为占位符使用。
- ObjectWritable类和GenericWritable类:适用于字段可以使用多种类型,其为一种多用途的封装,用于Hadoop的RPC来封送和反封送方法参数和返回类型
- Writable集合
- Writable集合类型:ArrayWritable,TwoDArrayWritable,MapWritable和SortedMapWritable
- ArrayWritable:数组
- TwoDArrayWritable:二维数组
- MapWritable
- SortedMapWritable
- 序列化框架
- API:Hadoop提供了一个简单的序列化框架API。序列化框架由Serialization实现(在org.apache.hadoop.io.serializer包)来表示。Serialization定义了从类型到Serializer实例(一个对象转为一个字节流)和Deserializer实例(从一个字节流转为一个对象)的映射
- IDL:接口描述语言,通过语言中立、声明式来定义它们。Apache Thrift和Google Protocol Buffers
四、基于文件的数据结构
- SequenceFile类
- 假设一个日志文件中的每一个日志记录都是一行新的文本。如果想记录二进制类型,纯文本并不是一个合适的格式。Hadoop的SequenceFile类很适用这种情况,其做法是为二进制键/值提供一个持久化的数据结构,当时SequenceFile类用于日志,需要一个键和一个值。
- 创建SequenceFile类
public class SequenceFileWriteDemo {
private static final String[] DATA ={
"One,two,buckle my shoe",
"Three,four,shut the door",
"Five,six,pick up sticks",
"Seven,eight,lay them straight",
"Nine,ten,a big fat hen"
}; public static void main(String[] args) throws IOException {
String uri = args[0];
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri),conf); IntWritable key = new IntWritable();
Text value = new Text();
SequenceFile.Writer writer = null;
try{
writer = SequenceFile.createWriter(fs,conf,path,key.getClass(),value.getClass());
for(int i = 0; i < 100;i++){
key.set(100-i);
value.set(DATA[i%DATA.length]);
System.out.println("[%s]\t%s\t%s\n",writer.getLength(),key,value);
}
}finally{
IOUtils.closeStream(writer);
} } }
- 读取SequeceFile类
- MapFile类
- 写入MapFile类
- 读取MapFile类