Key排序

1. 继承WritableComparator

  在hadoop之Shuffle和Sort中,可以看到mapper的输出文件spill文件需要在内存中排序,并且在输入reducer之前,不同的mapper的数据也会排序,排序是根据数据的key进行的.

如果key是用户自定义的类型,并没有默认的比较函数时,就需要自己定义key的比较函数,也就是继承WritableComparator.事例代码如下:

public static class KeyComparator extends WritableComparator {
protected KeyComparator() {
super(IntPair.class, true);
}
@Override
public int compare(WritableComparable w1, WritableComparable w2) {
IntPair ip1 = (IntPair) w1;
IntPair ip2 = (IntPair) w2;
// 这里要注意的是,一定要在聚合参数相同的情况下,再比较另一个参数
// 这里是先比较年份,再比较温度,按温度降序排序
int cmp = IntPair.compare(ip1.getFirst(), ip2.getFirst());
if (cmp != 0) {
return cmp;
}
return -IntPair.compare(ip1.getSecond(), ip2.getSecond()); //reverse
}
}

例子中对IntPair定义了新的compare函数,并在main函数中通过下面的方式实现替换:

job.setSortComparatorClass(KeyComparator.class);

 2.实现 WritableComparable接口

看下面的例子代码:

    static class  NewK2 implements WritableComparable<NewK2>{
Long first;
Long second; public NewK2(){}
public NewK2(long first, long second){
this.first = first;
this.second = second;
} @Override
public void readFields(DataInput in) throws IOException {
this.first = in.readLong();
this.second = in.readLong();
}
@Override
public void write(DataOutput out) throws IOException {
out.writeLong(first);
out.writeLong(second);
}
/**
* 当k2进行排序时,会调用该方法.
* 当第一列不同时,升序;当第一列相同时,第二列升序
*/
@Override
public int compareTo(NewK2 o) {
final long minus = this.first - o.first;
if(minus !=0){
return (int)minus;
}
return (int)(this.second - o.second);
} @Override
public int hashCode() {
return this.first.hashCode()+this.second.hashCode();
} @Override
public boolean equals(Object obj) {
if(!(obj instanceof NewK2)){
return false;
}
NewK2 oK2 = (NewK2)obj;
return (this.first==oK2.first)&&(this.second==oK2.second);
}
}

如果是按照上述的例子实现的,不需要在main函数中设置其他的代码.

Group排序

  一般来说,如果用户自定义了key的排序过程,那么在reducer之前的对数据进行分组的过程就要重新编写,而且一般来说,partitioner也需要重新定义,请参考hadoop之定制自己的Partitioner .

shuffle阶段,虽然使用的是hash的方法,我们并不能保证映射到同一个reducer的key的hash值都是一样的,对于不同的hash值要进行分群,然后再执行reduce.下面是自定义groupcomparator的例子:

  public static class GroupComparator extends WritableComparator {
protected GroupComparator() {
super(IntPair.class, true);
}
@Override
public int compare(WritableComparable w1, WritableComparable w2) {
IntPair ip1 = (IntPair) w1;
IntPair ip2 = (IntPair) w2;
// 这里是按key的第一个参数来聚合,就是年份
return IntPair.compare(ip1.getFirst(), ip2.getFirst());
}
}

例子中实现了对于IntPair类型的分群比较函数的重新定义.在main函数中通过下面的方式进行调用:

job.setGroupingComparatorClass(GroupComparator.class);

二次排序

  下面是对地区温度进行的统计,要求输出各个年份的最大温度,例子中定制了自己的partitioner:FirstPartitioner来对组合后的类型进行分组,实际上还是按照年份进行的分组;定制了自己的keycomparator:KeyComparator,先比较年份,然后再比较温度;定制了自己的分群比较类:GroupComparator,也是按照年份进行分群,然后扔给reducer进行处理.

  值得一提的是,为什么不用传统的mapreduce,按照年份进行进行map,然后在reduce中,遍历每年不同的温度,找到最大值呢?原因之一就是效率的问题,sort操作本身就要在MP框架中执行,而且已经做了很多优化,通过设置比较的不同手段,很容易实现比较,然而在reducer处理中进行遍历,显然比上面的sort过程要慢.下面是例子的完整代码,摘自Hadoop- The Definitive Guide, 4th Edition.

public class MaxTemperatureUsingSecondarySort extends Configured implements Tool {

  // Map任务
static class MaxTemperatureMapper extends MapReduceBase implements Mapper<LongWritable, Text, IntPair, NullWritable> {
private NcdcRecordParser parser = new NcdcRecordParser();
public void map(LongWritable key, Text value,
OutputCollector<IntPair, NullWritable> output, Reporter reporter)
throws IOException {
parser.parse(value); // 解析输入的文本
if (parser.isValidTemperature()) {
// 这里把年份与温度组合成一个key,value为空
output.collect(new IntPair(parser.getYearInt(),+ parser.getAirTemperature()), NullWritable.get());
}
}
} // Reduce任务
static class MaxTemperatureReducer extends MapReduceBase
implements Reducer<IntPair, NullWritable, IntPair, NullWritable> {
public void reduce(IntPair key, Iterator<NullWritable> values,
OutputCollector<IntPair, NullWritable> output, Reporter reporter)
throws IOException {
// 输出聚合的key值,这里的key是先按年份进行聚合,所我们会看到相同所有年份相同的key会聚合在一起,而这些聚合后的key按温度进行降序按列
// 所以聚合中第一个key为温度最高的,所以这里输出的key为这一年中温度最高的值
output.collect(key, NullWritable.get());
}
} // 切分器,这里是按年份* 127 % reduceNum来进行切分的
public static class FirstPartitioner
implements Partitioner<IntPair, NullWritable> {
@Override
public void configure(JobConf job) {}
@Override
public int getPartition(IntPair key, NullWritable value, int numPartitions) {
return Math.abs(key.getFirst() * 127) % numPartitions;
}
} // 聚合key的一个比较器
public static class KeyComparator extends WritableComparator {
protected KeyComparator() {
super(IntPair.class, true);
}
@Override
public int compare(WritableComparable w1, WritableComparable w2) {
IntPair ip1 = (IntPair) w1;
IntPair ip2 = (IntPair) w2;
// 这里要注意的是,一定要在聚合参数相同的情况下,再比较另一个参数
// 这里是先比较年份,再比较温度,按温度降序排序
int cmp = IntPair.compare(ip1.getFirst(), ip2.getFirst());
if (cmp != 0) {
return cmp;
}
return -IntPair.compare(ip1.getSecond(), ip2.getSecond()); //reverse
}
}
// 设置聚合比较器
public static class GroupComparator extends WritableComparator {
protected GroupComparator() {
super(IntPair.class, true);
}
@Override
public int compare(WritableComparable w1, WritableComparable w2) {
IntPair ip1 = (IntPair) w1;
IntPair ip2 = (IntPair) w2;
// 这里是按key的第一个参数来聚合,就是年份
return IntPair.compare(ip1.getFirst(), ip2.getFirst());
}
}
@Override
public int run(String[] args) throws IOException {
Job job = JobBuilder.parseInputAndOutput(this, getConf(), args);
if (job == null) {
return -1;
}
job.setMapperClass(MaxTemperatureMapper.class); job.setPartitionerClass(FirstPartitioner.class);
job.setSortComparatorClass(KeyComparator.class);
job.setGroupingComparatorClass(GroupComparator.class
);

job.setReducerClass(MaxTemperatureReducer.class);
job.setOutputKeyClass(IntPair.class); // 设置key的一个组合类型,如里这个类型实现了WritableComparable<T>的话,那就不要设置setOutputKeyComparatorClass了.
job.setOutputValueClass(NullWritable.class); // 输出的value为NULL,因为这里的实际value已经组合到了key中

return job.waitForCompletion(true) ? 0 : 1;
} public static void main(String[] args) throws Exception {
int exitCode = ToolRunner.run(new MaxTemperatureUsingSecondarySort(), args);
System.exit(exitCode);
}
}

hadoop之定制自己的sort过程的更多相关文章

  1. hadoop之定制自己的Partitioner

    partitioner负责shuffle过程的分组部分,目的是让map出来的数据均匀分布在reducer上,当然,如果我们不需要数据均匀,那么这个时候可以自己定制符合要求的partitioner. 下 ...

  2. Hadoop学习总结之Map-Reduce的过程解析111

    一.客户端 Map-Reduce的过程首先是由客户端提交一个任务开始的. 提交任务主要是通过JobClient.runJob(JobConf)静态函数实现的: public static Runnin ...

  3. Hadoop学习总结之Map-Reduce的过程解析

    一.客户端 Map-Reduce的过程首先是由客户端提交一个任务开始的. 提交任务主要是通过JobClient.runJob(JobConf)静态函数实现的: public static Runnin ...

  4. Hadoop入门程序WordCount的执行过程

    首先编写WordCount.java源文件,分别通过map和reduce方法统计文本中每个单词出现的次数,然后按照字母的顺序排列输出, Map过程首先是多个map并行提取多个句子里面的单词然后分别列出 ...

  5. 使用beanstalkd实现定制化持续集成过程中pipeline

    持续集成是一种项目管理和流程模型,依赖于团队中各个角色的配合.各个角色的意识和配合不是一朝一夕能练就的,我们的工作只是提供一种方案和能力,这就是持续集成能力的服务化.而在做持续集成能力服务化的过程中, ...

  6. Hadoop之——分布式集群安装过程简化版

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46352315 1.hadoop的分布式安装过程 1.1 分布结构 主节点(1个,是 ...

  7. Hadoop完全分布式安装配置完整过程

    一. 硬件.软件准备 1. 硬件设备 为了方便学习Hadoop,我采用了云服务器来配置Hadoop集群.集群使用三个节点,一个阿里云节点.一个腾讯云节点.一个华为云节点,其中阿里云和腾讯云都是通过使用 ...

  8. 视频演示eworkflow集成定制aspx页面的过程

    eworkflow自定义工作流系统,集成eform自定义表单,可以做到在线编辑流程,在线编辑表单.eform也提供在线建立业务表,维护表字段等,所以通过eworkflow+eform可以在线完成业务流 ...

  9. Hadoop集群搭建的详细过程

    Hadoop集群搭建 一.准备 三台虚拟机:master01,node1,node2 时间同步 1.date命令查看三台虚拟机时间是否一致 2.不一致时间同步:ntpdate ntp.aliyun.c ...

随机推荐

  1. openfalcon-0.2 配置

    hbs 配置文件 { "debug": true, "database": "openfalcon:123456@tcp(172.16.230.188 ...

  2. Java和Python安装和编译器使用

    java 一.安装jdk时第二次选择安装目录时,这是在安装jre,可以直接取消. 二.配置三个环境变量(在WIN7下) 右键我的电脑>属性>高级系统设置>环境变量>系统变量. ...

  3. python基础整理6——爬虫基础知识点

    爬虫基础 什么是爬虫: 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁. ...

  4. Azure云 windows平台 搭建ftp服务器注意事项

    1.iis设置防火墙支持端口(1-65535自定义端口,一般3-5个都行) 2.客户端连接使用被动链接模式 3.endpoint终结点添加20,21,以及你自定义的防火墙支持端口. 4.本地防火墙添加 ...

  5. 1.1《想成为黑客,不知道这些命令行可不行》(Learn Enough Command Line to Be Dangerous)——运行终端

    终端是个允许我们运行命令行的程序,运行命令前,先打开它.在MacOS系统上,可以使用macOS应用 Spotlight来打开终端窗口,Spotlight也有其他两种方式触发,一种是键入⌘␣(comma ...

  6. MAC系统常用快捷键 基本常用的都整理在这里了

    写在前面 Mac系统中有几个比较特殊的功能键,和Win系统的区别也主要在这里比如在Win系统中我们常用的Ctrl键,在Mac系统中对应的不是长得比较像的Cnotrol,而是Command键,貌似也是M ...

  7. PHP中const,static,public,private,protected的区别

    原文地址:http://small.aiweimeng.top/index.php/archives/54.html const: 定义常量,一般定义后不可改变static: 静态,类名可以访问pub ...

  8. 7.Classes-类(Dart中文文档)

    Dart是一个面向对象的语言,同时增加了混入(mixin)继承的特性.对象都是由类初始化生成的,所有的类都由Object对象继承.混入继承意味着尽管所有类(除了Object类)只有一个父类,但是类的代 ...

  9. 2015306 白皎 《网络攻防》Exp5 MSF基础应用

    2015306 白皎 <网络攻防>Exp5 MSF基础应用 一.基础问题 用自己的话解释什么是exploit,payload,encode. exploit指由攻击者或渗透测试者利用一个系 ...

  10. WPF之ListView使用WrapPanel

    原文:WPF之ListView使用WrapPanel 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/jiuzaizuotian2014/articl ...