适合Hadoop的数据类型
Hadoop使用派生于Writable接口的类作为MapReduce计算的数据类型,这些数据类型用于整个MapReduce计算流的数据吞吐过程,这个过程从读取输入数据开始,到传输map和reduce任务之间的中间数据,一直到最后写入输出数据为止;为输入数据、中间数据和输出数据选择合适的Writable数据类型面对MapReduce程序的可编程性和性能有很大的影响
为了用作MapReduce计算的value数据类型,数据类型必须实现
org.apache.hadoop.io.Writable
接口;Writable接口定义了当需要数据传输和数据存储时,Hadoop应该如何序列化和反序列化为了用作MapReduce计算的key数据类型,数据类型必须实现
org.apache.hadoop.io.WritableComparable<T>
接口,除了Writable接口的功能之外,有一种WritableComparable接口更进一步定义了如何将这种类型的键相互比较,以达到排序的目的
Hadoop带有一些预定于的类用于实现WritableComparable,包括基本数据类型的封装类:
类 | 描述 |
---|---|
BooleanWritable | 标准布尔变量的封装 |
ByteWritable | 单字节数的封装 |
DoubleWritable | 双字节数的封装 |
FloatWritable | 浮点数的封装 |
IntWritable | 整数的封装 |
LongWritable | 长整型的封装 |
Text | 使用UTF-8格式的文本封装 |
NullWritable | 无键值时的占位符 |
1.使用泛型类型变量为mapper的键值对指定输入数据类型(键:LongWritable,值:Text)和输出数据类型(键:Text,值:IntWritable)
public class SampleMapper extends Mapper<LongWritable,Text,Text,IntWritable>{
public void map(LongWritable key, Text value,Context context). . .{
. . .
}
}
2.使用通用型变量为reducer的键值对指定输入数据类型(键:Text,值:IntWritable)和输出数据类型(键:Text,值:IntWritable)【reducer的输入键值对数据类型应该和mapper的输出键值对数据类型相匹配】
public class Reduce extends Reducer<Text,IntWritable,Text,IntWritable>{
public void reduce(Text key, IntWritable value,Context context). . .{
. . .
}
}
3.使用Job对象指定MapReduce计算输出数据类型,当mapper和reducer的输出类型相同时:
Job job = new Job(. . .);
(Job job = Job.getInstance();)
. . .
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
4.当mapper和reducer的输出类型相同时:
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
Hadoop提供一些基本数据类型,IntWritable,LongWritable,BooleanWritable,FloatWritable,ByteWritable,这是它们各自的Java基本数据类型的Writable版本,可以使用这些类型作为key类型和value类型
下面是几种Hadoop内置的数据类型,可以作为作为key类型和value类型:
Text:存储UTF8文本
BytesWritable:存储一个字节序列
VIntWritable和VLongWritable:存储变长整型和长整型值
NullWritable:这是零长度的Writable类型(可以在不希望使用key或value类型的时候使用)
下面是Hadoop内置的集合数据类型【只能作为value类型】:
ArrayWritable:存储属于Writable类型的值数组(要使用ArrayWritable类型作为reduce输入的value类型,需要创建ArrayWritable的子类来指定存储在其中的Writable值的类型):
public class LongArrayWritable extends ArrayWritable{
public LongArrayWritable(){
super(LongWritable.class);
}
}
TwoDArrayWritable:存储属于同一个Writable类型的值矩阵(要使用TwoDArrayWritable类型作为reduce输入的value类型,需要创建与ArrayWritable类型相似的TwoDArrayWritable类型的子类来指定存储的值的类型)
MapWritable:存储键值对的映射(键和值应该是Writable数据类型)
SortedMapWritable:存储键值对的有序映射(键应该事先WritableComparable接口)
实现自定义的Hadoop Writable数据类型
通过org.apache.hadoop.io.Writable
接口编写一个定制Writable数据类型用于定义数据类型的序列化格式(基于Writable的接口类型可以用来作为Hadooop MapReduce计算的value类型)
假设日志包含5个部分:请求的主机,时间戳,请求的URL,相应大小,HTTP状态码,如下:
192.168.0.2 -- [01/Jul.1995:00:00:01-0400] "GET/history/appollo/HTTP/1.0" 200 6245
实现日志条目的自定义Hadoop Writable数据类型的步骤:
1.血一个新的LogWritable类实现org.apache.hadoop.io.Writable接口
public class LogWritable implements Writable{
private Text userIP;
private Text timestamp;
private Text request;
private IntWritable responseSize;
private IntWritable status;
public LogWritable(){
this.userIP = new Text();
this.timestamp = new Text();
this.request = new Text();
this.responseSize = new IntWritable();
this.status = new IntWritable();
}
public void readFields(DataInput in)throws IOException{
userIP.readFields(in);
timestamp.readFields(in);
request.readFields(in);
responseSize.readFields(in);
status.readFields(in);
}
public void write(DataOuput out)throws IOException{
userIP.write(out);
timestamp.write(out);
request.write(out);
responseSize.write(out);
status.write(out);
}
. . .//getters and setters for the fields
}
2.使用新的LogWritable类型作为MapReduce计算的value类型
例如,使用LogWritable类型作为Map输出值的类型
public class LogProcessMap extends Mapper<LongWritable,Text,Text,LogWritable>{
...
}
public class LogProcessReduce extends Reducer<Text,LogWritable,Text,IntWritable>{
public void reduce(Text key,Iterable<LogWritable> values,Context context){
...
}
}
3.配置相应的作用的输出类型
Job job = new Job(...);
(Job job = Job.getInstance();)
...
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LogWritable.class);
工作原理
Writable接口包含2个方法:readFields()he write(),在readFields()方法中我们反序列化输入数据,并填充Writable对象的字段:
public void readFields(DataInput in)throws IOException{
userIP.readFields(in);
timestamp.readFields(in);
request.readFields(in);
responseSize.readFields(in);
status.readFields(in);
}
在上面的示例中,使用Writable类型作为自定义的Writable类型的字段,并且使用readFields()方法从DataInput对象的数据字段中反序列化数据,【当然也可以使用Java的基本数据类型作为Writabel类型的字段,并使用DataInput对象的想要读取方法从基础流中读取值】,如下面的代码所示:
int responseSize = in.readInt();
String userIP = in.readUTF();