一起学Hadoop——二次排序算法的实现
二次排序,从字面上可以理解为在对key排序的基础上对key所对应的值value排序,也叫辅助排序。一般情况下,MapReduce框架只对key排序,而不对key所对应的值排序,因此value的排序经常是不固定的。但是我们经常会遇到同时对key和value排序的需求,例如Hadoop权威指南中的求一年的高高气温,key为年份,value为最高气温,年份按照降序排列,气温按照降序排列。还有水果电商网站经常会有按天统计水果销售排行榜的需求等等,这些都是需要对key和value同时进行排序。如下图所示:
如何设计一个MapReduce程序解决对key和value同时排序的需求呢?这就需要用到组合键、分区、分组的概念。在这里又看到分区的影子,可知分区在MapReduce是多么的重要,一定要好好掌握,是优化的重点。
按照上图中数据流转的方向,我们首先设计一个Fruit类,有三个字段,分别是日期、水果名和销量,将日期、水果名和销量作为一个复合键;接着设计一个自定义Partition类,根据Fruit的日期字段分区,让相同日期的数据流向同一个partition分区中;最后定义一个分组类,实现同一个分区内的数据分组,然后按照销量字段进行二次排序。
具体实现思路:
1、定义Fruit类,实现WritableComparable接口,并且重写compareTo、equal和hashcode方法以及序列化和反序列化方法readFields和write方法。Java类要在网络上传输必须序列化和反序列化。在Map端的map函数中将Fruit对象当做key。compareTo方法用于比较两个key的大小,在本文中就是比较两个Fruit对象的排列顺序。
2、自定义第一次排序类,继承WritableComparable或者WritableComparator接口,重写compareTo或者compare方法,。就是在Map端对Fruit对象的第一个字段进行排序
3、自定义Partition类,实现Partitioner接口,并且重写getPartition方法,将日期相同的Fruit对象分发到同一个partition中。
4、定义分组类,继承WritableComparator接口,并且重写compare方法。用于比较同一分组内两个Fruit对象的排列顺序,根据销量字段比较。日期相同的Fruit对象会划分到同一个分组。通过setGroupingComparatorClass方法设置分组类。如果不设置分组类,则按照key默认的compare方法来对key进行排序。
代码如下:
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.Partitioner;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class SecondrySort extends Configured implements Tool { static class Fruit implements WritableComparable<Fruit>{
private static final Logger logger = LoggerFactory.getLogger(Fruit.class);
private String date;
private String name;
private Integer sales;
public Fruit(){
}
public Fruit(String date,String name,Integer sales){
this.date = date;
this.name = name;
this.sales = sales;
} public String getDate(){
return this.date;
} public String getName(){
return this.name;
} public Integer getSales(){
return this.sales;
} @Override
public void readFields(DataInput in) throws IOException{
this.date = in.readUTF();
this.name = in.readUTF();
this.sales = in.readInt();
} @Override
public void write(DataOutput out) throws IOException{
out.writeUTF(this.date);
out.writeUTF(this.name);
out.writeInt(sales);
} @Override
public int compareTo(Fruit other) {
int result1 = this.date.compareTo(other.getDate());
if(result1 == 0) {
int result2 = this.sales - other.getSales();
if (result2 == 0) {
double result3 = this.name.compareTo(other.getName());
if(result3 > 0) return -1;
else if(result3 < 0) return 1;
else return 0;
}else if(result2 >0){
return -1;
}else if(result2 < 0){
return 1;
}
}else if(result1 > 0){
return -1;
}else{
return 1;
}
return 0;
} @Override
public int hashCode(){
return this.date.hashCode() * 157 + this.sales + this.name.hashCode();
} @Override
public boolean equals(Object object){
if (object == null)
return false;
if (this == object)
return true;
if (object instanceof Fruit){
Fruit r = (Fruit) object;
// if(r.getDate().toString().equals(this.getDate().toString())){
return r.getDate().equals(this.getDate()) && r.getName().equals(this.getName())
&& this.getSales() == r.getSales();
}else{
return false;
}
} public String toString() {
return this.date + " " + this.name + " " + this.sales;
} } static class FruitPartition extends Partitioner<Fruit, NullWritable>{
@Override
public int getPartition(Fruit key, NullWritable value,int numPartitions){
return Math.abs(Integer.parseInt(key.getDate()) * 127) % numPartitions;
}
} public static class GroupingComparator extends WritableComparator{
protected GroupingComparator(){
super(Fruit.class, true);
} @Override
public int compare(WritableComparable w1, WritableComparable w2){
Fruit f1 = (Fruit) w1;
Fruit f2 = (Fruit) w2; if(!f1.getDate().equals(f2.getDate())){
return f1.getDate().compareTo(f2.getDate());
}else{
return f1.getSales().compareTo(f2.getSales());
}
}
} public static class Map extends Mapper<LongWritable, Text, Fruit, NullWritable> { public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String str[] = line.split(" ");
Fruit fruit = new Fruit(str[0],str[1],new Integer(str[2]));
//Fruit fruit = new Fruit();
//fruit.set(str[0],str[1],new Integer(str[2]));
context.write(fruit, NullWritable.get());
}
} public static class Reduce extends Reducer<Fruit, NullWritable, Text, NullWritable> { public void reduce(Fruit key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
String str = key.getDate() + " " + key.getName() + " " + key.getSales();
context.write(new Text(str), NullWritable.get());
}
} @Override
public int run(String[] args) throws Exception {
Configuration conf = new Configuration();
// 判断路径是否存在,如果存在,则删除
Path mypath = new Path(args[1]);
FileSystem hdfs = mypath.getFileSystem(conf);
if (hdfs.isDirectory(mypath)) {
hdfs.delete(mypath, true);
} Job job = Job.getInstance(conf, "Secondry Sort app");
// 设置主类
job.setJarByClass(SecondrySort.class); // 输入路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
// 输出路径
FileOutputFormat.setOutputPath(job, new Path(args[1])); // Mapper
job.setMapperClass(Map.class);
// Reducer
job.setReducerClass(Reduce.class); // 分区函数
job.setPartitionerClass(FruitPartition.class); // 分组函数
job.setGroupingComparatorClass(GroupingComparator.class); // map输出key类型
job.setMapOutputKeyClass(Fruit.class);
// map输出value类型
job.setMapOutputValueClass(NullWritable.class); // reduce输出key类型
job.setOutputKeyClass(Text.class);
// reduce输出value类型
job.setOutputValueClass(NullWritable.class); // 输入格式
job.setInputFormatClass(TextInputFormat.class);
// 输出格式
job.setOutputFormatClass(TextOutputFormat.class); return job.waitForCompletion(true) ? 0 : 1;
} public static void main(String[] args) throws Exception{
int exitCode = ToolRunner.run(new SecondrySort(), args);
System.exit(exitCode);
}
}
测试数据:
20180906 Apple 200
20180904 Apple 200
20180905 Banana 100
20180906 Orange 300
20180906 Banana 400
20180904 Orange 100
20180905 Apple 400
20180904 Banana 300
20180905 Orange 500
运行结果:
20180906 Banana 400
20180906 Orange 300
20180906 Apple 200
20180905 Orange 500
20180905 Apple 400
20180905 Banana 100
20180904 Banana 300
20180904 Apple 200
20180904 Orange 100
总结:
1、在使用实现WritableComparable接口的方式实现自定义比较器时,必须有一个无参的构造函数。否则会报Unable to initialize any output collector的错误。
2、readFields和write方法中处理字段的顺序必须一致,否则会报MapReduce Error: java.io.EOFException at java.io.DataInputStream.readFully(DataInputStream.java:197)的错误。
了解更多大数据的知识请关注我的微信公众号:summer_bigdata
欢迎可以扫码关注本人的公众号:
一起学Hadoop——二次排序算法的实现的更多相关文章
- Python学习(三) 八大排序算法的实现(下)
本文Python实现了插入排序.基数排序.希尔排序.冒泡排序.高速排序.直接选择排序.堆排序.归并排序的后面四种. 上篇:Python学习(三) 八大排序算法的实现(上) 1.高速排序 描写叙述 通过 ...
- 排序算法的实现之Javascript(常用)
排序算法的实现之Javascript 话不多说,直接代码. 1.冒泡排序 1.依次比较相邻的两个数,如果前一个比后一个大,则交换两者的位置,否则位置不变 2.按照第一步的方法重复操作前length-1 ...
- hadoop 二次排序的一些思考
先说一下mr的二次排序需求: 假如文件有两列分别为name.score,需求是先按照name排序,name相同按照score排序 数据如下: jx 20 gj 30 jx 10 gj 15 输出结果要 ...
- Hadoop 二次排序
需求 求每年的最高气温,年份升序,温度求最高 数据源内容如下 temperature.txt 2004 49 1981 -22 1981 -31 1965 -47 2027 -2 1964 6 203 ...
- hadoop 二次排序的思考
name sorce jx 10 gj 15 jx 20 gj 30 1. 二次排序 key如何定义 2. grouping 是不是一定要实现,不实现可以吗? 3. 二次排序的本质是什么 4. 如果以 ...
- 各类排序算法的实现C#版
using System;using System.CodeDom;using System.Collections.Generic;using System.Linq;using System.Ru ...
- 排序算法的实现(归并,快排,堆排,希尔排序 O(N*log(N)))
今天跟着左老师的视频,理解了四种复杂度为 O(N*log(N))的排序算法,以前也理解过过程,今天根据实际的代码,感觉基本的算法还是很简单的,只是自己写的时候可能一些边界条件,循环控制条件把握不好. ...
- hadoop二次排序
import java.io.DataInput; import java.io.DataOutput; import java.io.File; import java.io.IOException ...
- Javascript十大排序算法的实现方法
上一篇中,实现了Javascript中的冒泡排序方法,下面把剩余的九种排序算法实现 选择排序: var array = []; for(var i=0;i<100000;i++){ var x ...
随机推荐
- zabbix-3.0.4添加对windows 2008r2的监控
zabbix-3.0.4添加对windows 2008r2的监控 一.windows客户端的配置关闭windows防火墙或者开通10050和10051端口(1).关闭防火墙(不推荐直接关闭,测试可以这 ...
- Day8--------------源码安装
yun install python 在yum源中 wget 地址 下载 下载---------->解包-------->运行config脚本添加编译参数--------->编译(g ...
- 面向对象(metaclass继承高级用法)
方法一:# class MyType(type):# def __init__(self,*args,**kwargs):# print('132')# super(MyType,self).__in ...
- Bootstrap 固定底部导航栏菜单
直接上代码: <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" ...
- GDOI2018 涛涛摘苹果 [CDQ分治]
传送门我会让你知道哪里有题面吗(逃 思路 显然不能模拟苹果下掉的过程,考虑计算每个苹果对询问的贡献. 显然一开始就有的苹果可以看做第0天变出来的,于是只需要考虑变出来的苹果了. 设当前询问节点\(x\ ...
- Codeforces 1110D Jongmah [DP]
洛谷 Codeforces 我-我我把这-这这题切了??? 说实话这题的确不难,只是我看到有大佬没做出来有点慌-- 突然发现这题是我在洛谷的第500个AC呢.那就更要写篇题解纪念一下了. 思路 容易想 ...
- 开发了5年android,我开始了go学习之旅
前言 做了近5年的android开发,最近项目也是不怎么忙,空闲的时候总会思考一些事情,不过作为移动开发,我个人觉得很有必要学习后台开发,由于公司是Go语言开发的,了解go语言一段时间后,我发现go语 ...
- 软件测试作业 - fault error failure
给出的题目如下: 我的解答如下: For program 1:1. where i > 0 is the fault , it should be changed to i>= 0 to ...
- Confluence 6 管理应用服务器内存设置
应用服务器中的最小和最大 JVM Heap 空间配置将会影响系统的性能.Confluence 管理员可能希望对默认的配置进行修改,基于你系统的负载不同配置情况也会有所不同,请参考页面 Server H ...
- Confluence 6 通过 SSL 或 HTTPS 运行 - 修改你 Confluence 的 server.xml 文件
下一步你需要配置 Confluence 来使用 HTTPS: 编辑 <install-directory>/conf/server.xml 文件. 取消注释下面的行: <Connec ...