从零开始大数据【1.4】-- mapreduce中的排序+自定义WritableComparable
文章目录
1 上期回顾:
第二节中,我们定义了一个新的数据类型作为mapreduce中的数据类型,当时把这个数据类型当做value值,所以没有涉及到排序,现在我们准备将他作为key值,所以来写它的WritableComparable。
2 mapreduce中的排序
排序是 MapReduce 框架中最重要的操作之一。Map Task和 Reduce Task均会对数据(按照 key)进行排序。该操作属于 Hadoop 的默认行为。任何应用程序中的数据均会被排序,而不管逻辑上是否需要。默认排序是按照字典顺序排序,实现该排序的方法是快速排序。
对于 Map Task,它会将处理的结果暂时放到一个缓冲区中,当缓冲区使用率达到一定阈值后,再对缓冲区中的数据进行一次排序,并将这些有序数据写到磁盘上,而当数据处理完毕后,它会对磁盘上所有文件进行一次合并,以将这些文件合并成一个大的有序文件。
对于 Reduce Task,它从每个 Map Task上远程拷贝相应的数据文件,如果文件大小超过一定阈值,则放到磁盘上,否则放到内存中。如果磁盘上文件数目达到一定阈值,则进行一次合并以生成一个更大文件;如果内存中文件大小或者数目超过一定阈值,则进行一次合并后将数据写到磁盘上。当所有数据拷贝完毕后,Reduce Task 统一对内存和磁盘上的所有数 据进行一次合并。
可以看到,对key的排序多次出现在mapreduce程序中,接下来就来说怎么自定义排序的方式。
3 实际案例
仍然采用之前的数据集,这一次,希望输出的结果是按照reviews的数量排序输出的,为了完成这个目的,首先要在自定义的bean对象中添加WritableComparable。
4 编写bean对象
主要是变成implements WritableComparable并且重写了compareTo方法。
package appmapreduce;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;
public class nbean implements WritableComparable<nbean>{
private long reviews;
private double rating;
//空参构造(在source中 generate construct using fields)
public nbean() {
super();
}
public nbean(long reviews, double rating) {
super();
this.reviews = reviews;
this.rating = rating;
}
@Override
public String toString() {
return reviews +"\t"+rating;
}
//反序列化方法
@Override
public void readFields(DataInput in) throws IOException {
// TODO Auto-generated method stub
this.rating = in.readLong();
this.reviews = in.readLong();
}
//序列化方法
@Override
public void write(DataOutput out) throws IOException {
// TODO Auto-generated method stub
out.writeDouble(rating);
out.writeLong(reviews);
}
public long getReviews() {
return reviews;
}
public void setReviews(long reviews) {
this.reviews = reviews;
}
public double getRating() {
return rating;
}
public void setRating(double rating) {
this.rating = rating;
}
public void set(long reviews,double rating) {
this.reviews = reviews;
this.rating = rating;
}
@Override
public int compareTo(nbean o) {
// TODO Auto-generated method stub
return (int)(this.reviews-o.getReviews());
}
}
5 mapreduce编程
要完成上述的例子,同样需要完成三个程序分别是一个mapper类、一个reducer类和一个用于连接整个过程的驱动driver主程序。
5.1 mapper
新建的mapper类基本不变只改变了输出的数据类型。
package appmapreduce;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
public class appmapper extends Mapper<LongWritable, Text, nbean, Text>{
nbean vall = new nbean();
Text kl = new Text();
@Override
protected void map(LongWritable key, Text value,Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
//获取一行
String line = value.toString();
//切割
String[] fields = line.split("\t");
System.out.println(fields);
//封装
String category = fields[1];
double rating = Double.parseDouble(fields[2]);
long reviews = Long.parseLong(fields[3]);
vall.set(reviews, rating);
kl.set(category);
//写出数据
context.write(vall, kl);
}
}
5.2 reducer
建立 reducer继承reducer类,重写reduce函数,这里的reducer只是把kv调换后输出。
package appmapreduce;
import java.io.IOException;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class appreducer extends Reducer<nbean, Text, Text, nbean>{
@Override
protected void reduce(nbean key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
/*double avgrating = 0;
long sumreviews = 0;
int count = 0;
//累加求和,累加求平均
for (nbean nbean : values) {
sumreviews = nbean.getReviews();
avgrating = nbean.getRating();
count +=1;
}
nbean newval = new nbean(sumreviews,avgrating);
//输出
* */
context.write(values.iterator().next(),key);
}
}
5.3 driver
只是更改了map的输出类型
package appmapreduce;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class appdriver {
public static void main(String[] args) throws IllegalArgumentException, IOException, ClassNotFoundException, InterruptedException {
//1 获取job对象
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration);
//2 设置jar路径
job.setJarByClass(appdriver.class);
//3 mapper reducer类
job.setMapperClass(appmapper.class);
job.setReducerClass(appreducer.class);
//4 mapper kv
job.setMapOutputKeyClass(nbean.class);
job.setMapOutputValueClass(Text.class);
//5reducer kv
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(nbean.class);
//8 分区
//job.setPartitionerClass(catpartitioner.class);
//job.setNumReduceTasks(2);
//6 输入输出路径
FileInputFormat.setInputPaths(job, new Path("e:/tyr"));
FileOutputFormat.setOutputPath(job, new Path("e:/oo2"));
//7 提交
boolean result = job.waitForCompletion(true);
}
}
5.4 运行
得到的输出结果如上图所示,可以发现自动按照reviews的重少到多排序了。
【欢迎大家和我一起学习!!!!】