2019独角兽企业重金招聘Python工程师标准>>>
序列化概念
序列化(Serialization)是指把结构化对象转化为字节流。
反序列化(Deserialization)是序列化的逆过程。即把字节流转回结构化对象。
Java序列化(java.io.Serializabl)
Hadoop序列化的特点
1.紧凑:高效使用存储空间。
2.快速:读写数据的额外开销小
3.可扩展:可透明地读取老格式的数据
4.互操作:支持多语言的交互
Hadoop序列化的作用
序列化在分布式环境的两大作用:进程间通信,永久存储。
Hadoop节点间通信。
自定义Writable类
class KpiWritable implements Writable{
int uppacknum;
int downpacknum;
int uppayload;
int downpayload;
public KpiWritable() {
super();
}
public void set(String uppacknum, String downpacknum, String uppayload,
String downpayload) {
this.uppacknum = Integer.parseInt(uppacknum);
this.downpacknum = Integer.parseInt(downpacknum);
this.uppayload = Integer.parseInt(uppayload);
this.downpayload = Integer.parseInt(downpayload);
}
public KpiWritable(int uppacknum, int downpacknum, int uppayload,
int downpayload) {
super();
this.uppacknum = uppacknum;
this.downpacknum = downpacknum;
this.uppayload = uppayload;
this.downpayload = downpayload;
}
public KpiWritable(String uppacknum, String downpacknum, String uppayload,
String downpayload) {
super();
this.set(uppacknum, downpacknum, uppayload, downpayload);
}
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(this.uppacknum);
out.writeInt(this.downpacknum);
out.writeInt(this.uppayload);
out.writeInt(this.downpayload);
}
@Override
public void readFields(DataInput in) throws IOException {
this.uppacknum = in.readInt();
this.downpacknum = in.readInt();
this.uppayload = in.readInt();
this.downpayload = in.readInt();
}
@Override
public String toString() {
return uppacknum +"\t"+ downpacknum +"\t"+uppayload +"\t"+downpayload;
}
}
Writable
1.write 是把每个对象序列化到输出流
2.readFields是把输入流字节反序列化
实现WritableComparable.
Java值对象的比较:一般需要重写toString(),hashCode(),equals()方法
基于文件的存储结构
SequenceFile 无序存储
MapFile 会对key建立索引文件,value按key顺序存储
基于MapFile的结构有:
ArrayFile 像我们使用的数组一样,key值为序列化的数字
SetFile 他只有key,value为不可变的数据
BloomMapFile 在 MapFile 的基础上增加了一个 /bloom 文件,包含的是二进制的过滤表,在每一次写操作完成时,会更新这个过滤表。
SequenceFile
SequeceFile是Hadoop API提供的一种二进制文件支持。这种二进制文件直接将
支持压缩,且可定制为基于Record或Block压缩(Block级压缩性能较优)
本地化任务支持:因为文件可以被切分,因此MapReduce任务时数据的本地化情况应该是非常好的。
对key、value的长度进行了定义,(反)序列化速度非常快。
缺点是需要一个合并文件的过程,文件较大,且合并后的文件将不方便查看,必须通过遍历查看每一个小文件。
public static void main(String[] args) throws Exception{
final Configuration conf = new Configuration();
final FileSystem fs = FileSystem.get(new URI("hdfs://hadoop:9000/"), conf);
//写操作
final Writer writer = new SequenceFile.Writer(fs, conf, new Path("/sf"), LongWritable.class, Text.class);
writer.append(new LongWritable(1), new Text("111"));
IOUtils.closeStream(writer);
//读操作
final SequenceFile.Reader reader = new SequenceFile.Reader(fs, new Path("/sf"), conf);
final LongWritable key = new LongWritable();
final Text val = new Text();
while (reader.next(key, val)) {
System.out.println(key.get()+"\t"+val.toString());
}
IOUtils.closeStream(reader);
}
MapFile
MapFile是排序后的SequenceFile,通过观察其目录结构可以看到MapFile由两部分组成,分别是data和index。
index作为文件的数据索引,主要记录了每个Record的key值,以及该Record在文件中的偏移位置。在MapFile被访问的时候,索引文件会被加载到内存,通过索引映射关系可迅速定位到指定Record所在文件位置,因此,相对SequenceFile而言,MapFile的检索效率是高效的,缺点是会消耗一部分内存来存储index数据。
需注意的是,MapFile并不会把所有Record都记录到index中去,默认情况下每隔128条记录存储一个索引映射。当然,记录间隔可人为修改,通过MapFIle.Writer的setIndexInterval()方法,或修改io.map.index.interval属性;
另外,与SequenceFile不同的是,MapFile的KeyClass一定要实现WritableComparable接口,即Key值是可比较的。
public static void main(String[] args) throws Exception{
final Configuration conf = new Configuration();
final FileSystem fs = FileSystem.get(new URI("hdfs://hadoop:9000/"), conf);
//写数据
final MapFile.Writer writer = new MapFile.Writer(conf, fs, "/aaa", Text.class, Text.class);
writer.append(new Text("1"), new Text("aa"));
IOUtils.closeStream(writer);
//读数据
final MapFile.Reader reader = new MapFile.Reader(fs, "/aaa", conf);
final Text key = new Text();
final Text val = new Text();
while(reader.next(key, val)) {
System.out.println(key.toString()+"\t"+val.toString());
}
}
小文件存储
注意:使用MapFile或SequenceFile虽然可以解决HDFS中小文件的存储问题,但也有一定局限性,如:
1.文件不支持复写操作,不能向已存在的SequenceFile(MapFile)追加存储记录
2.当write流不关闭的时候,没有办法构造read流。也就是在执行文件写操作的时候,该文件是不可读取的
MapReduce输入的处理类
FileInputFormat:
FileInputFormat是所有以文件作为数据源的InputFormat实现的基类,
FileInputFormat保存作为job输入的所有文件,并实现了对输入文件计算splits的方法。至于获得记录的方法是有不同的子类——TextInputFormat进行实现的。
InputFormat
InputFormat 负责处理MR的输入部分.
有三个作用:
验证作业的输入是否规范.
把输入文件切分成InputSplit.
提供RecordReader 的实现类,把InputSplit读到Mapper中进行处理.
InputSplit
◆ 在执行mapreduce之前,原始数据被分割成若干split,每个split作为一个map任务的输入,在map执行过程中split会被分解成一个个记录(key-value对),map会依次处理每一个记录。
◆ FileInputFormat只划分比HDFS block大的文件,所以FileInputFormat划分的结果是这个文件或者是这个文件中的一部分.
◆ 如果一个文件的大小比block小,将不会被划分,这也是Hadoop处理大文件的效率要比处理很多小文件的效率高的原因。
◆ 当Hadoop处理很多小文件(文件大小小于hdfs block大小)的时候,由于FileInputFormat不会对小文件进行划分,所以每一个小文件都会被当做一个split并分配一个map任务,导致效率底下。
例如:一个1G的文件,会被划分成16个64MB的split,并分配16个map任务处理,而10000个100kb的文件会被10000个map任务处理。
TextInputFormat
◆ TextInputformat是默认的处理类,处理普通文本文件。
◆ 文件中每一行作为一个记录,他将每一行在文件中的起始偏移量作为key,每一行的内容作为value。
◆ 默认以\n或回车键作为一行记录。
◆ TextInputFormat继承了FileInputFormat。
底层架构
其他输入类
◆ CombineFileInputFormat
相对于大量的小文件来说,hadoop更合适处理少量的大文件。
CombineFileInputFormat可以缓解这个问题,它是针对小文件而设计的。
◆ KeyValueTextInputFormat
当输入数据的每一行是两列,并用tab分离的形式的时候,KeyValueTextInputformat处理这种格式的文件非常适合。
◆ NLineInputformat
NLineInputformat可以控制在每个split中数据的行数。
◆ SequenceFileInputformat
当输入文件格式是sequencefile的时候,要使用SequenceFileInputformat作为输入。
Hadoop的输出处理类
TextOutputformat
默认的输出格式,key和value中间值用tab隔开的。
◆ SequenceFileOutputformat
将key和value以sequencefile格式输出。
◆ SequenceFileAsOutputFormat
将key和value以原始二进制的格式输出。
◆ MapFileOutputFormat
将key和value写入MapFile中。由于MapFile中的key是有序的,所以写入的时候必须保证记录是按key值顺序写入的。
◆ MultipleOutputFormat
默认情况下一个reducer会产生一个输出,但是有些时候我们想一个reducer产生多个输出,MultipleOutputFormat和MultipleOutputs可以实现这个功能。