Mapreduce中的 自定义类型、分组与二次排序

0、需求说明

数据格式
MR源数据

期望输出的结果
MR结果

做简单分析:
a. 由于只有两列,所以可以将map的InputFormat设置为KeyValueTextInputFormat
b. 事实上这里实现了两个排序,即对输出的key和value都做了排序,对于value相当于是分组排序,这种时候需要将key和value放在同一个对象中,所以自定义一个类型

1.自定义(key)类型

结果类型为 StrWritable(String strName,String strValue),并未实现构造方法,只是为了方便查看
完整的代码将放在最后面
将两列做为一个对象,自定义这个对象,继承WritableComparable,实现以下方法
方法的实现:
set、get方法 –>set方法在map中用来设置输入的key,故可以选择合并起来,get方法将在后续的reduce和排序中用来获取值
自定义类型实现序列化和反序列化
compareTo方法 –> 这个方法在排序中会用到,因为MR框架中的默认排序是使用key对象中的该方法进行的,所以如果不实现自己的排序方法的话,重写这个类是必要的
这里还有一些诸如toString、hashcode、equal等方法可以实现,根据需要选择,如在默认的分区中MR框架会调用hashcode方法

2、hadoop中的辅助排序

辅助排序分三种,分别为分区partitioner、key比较函数Comparator 、分组函数Grouping Comparator
在本文中只实现了后两者,类名分别为:StrNameComparator 和SortComparator
StrNameComparator 是对 StrWritable(String strName,String strValue)中的strName比较
SortComparator的处理是当传入的两个对象的strName不一致时,对strValue进行比较,结果都是返回int
完整的代码将放在最后面

a. partitioner

将满足相同条件的map结果分到同一个reduce中,默认是根据key的hashCode来分配的,重写只要实现getPartition方法即可
该类的作用是在map之后运行的
 在job调用中提供的方法是 job.setPartitionerClass(Partitioner p)

b. key Comparator

默认使用compareTo方法,在自定义这里重定义了排序方法,即以key中的第一列进行排序
在partitioner之后进行,会将相同key的value放到一起进行迭代
下图是一种在StrWritable(String strName,String strValue)中针对 strName实现的key排序
排序1
注意这里并没有对 str2实现排序
在job中提供的方法是
job.setSortComparatorClass(StrNameComparator.class)

c. Grouping Comparator

     在reduce阶段,会构造一个key对应的value迭代器,同一个reduce里面具有相同key的数据会被分到同一个组
     分组函数,注意这个分组函数的排序排的是对应的组,而不是结果。
     即如果要实现对分组的结果(如b中的strValue)进行排序的话,那么需要先对组(b中的strName)进行排序,分组函数设置的排序类既是这个
      然后再设置job的setSortComparatorClass进行strValue的比较,完整的设置如下
    job.setSortComparatorClass(SortComparator.class);  
                     //在b中,没有下面一行,并且这里设置的值是StrNameComparator.class
  job.setGroupingComparatorClass(StrNameComparator.class);

d. key比较函数与分组函数的补充说明

在b和c中出现的两个类 StrNameComparator 、SortComparator继承的类都是hadoop.io中的WritableComparator,每一个实现都需要对比较的对象进行注册,然后根据需要重写其中的compare方法,如下图
这里写图片描述
事实上在hadoop 中还提供了一种更原生的比较器 RawComparator,WritableComparator就是其子类,RawComparator的一个优势在与其可以直接对序列化的对象进行比较

还有就是在writablecomparable中对compare实现了多态,其中之一就是对字节的比较,本文中有偷懒,直接对字符串用compareTo进行比较
这里写图片描述

3、代码

a.StrWritable 自定义类

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.io.WritableComparable;

public class StrWritable implements WritableComparable<StrWritable>{
    private  String strName;
    private  String strValue;
    public void setstrName(String strName) {
        this.strName = strName;
    }
    public void setstrValue(String strValue) {
        this.strValue = strValue;
    }
    public String getstrName() {
        return strName;
    }
    public String getstrValue() {
        return strValue;
    }
    @Override
    public void write(DataOutput out) throws IOException {
        out.writeUTF(strName);
        out.writeUTF(strValue);
    }
    @Override
    public void readFields(DataInput in) throws IOException {
        strName =in.readUTF();
        strValue =in.readUTF();
    }
    @Override
    public int compareTo(StrWritable o) {
        //这里只对StrName进行比较,在Job中在没有分组函数情况下,默认是这个排序,也可以重新定义
        int  i= o.getstrName().compareTo(this.strName);
        //if(i!=0)
            return i;
        //return o.getstrName().compareTo(this.strName);

    }
}

b. Map函数

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class MapStr extends Mapper<Object, Text, StrWritable, IntWritable>{
    private StrWritable sw=new StrWritable();
    private IntWritable intWritable =new IntWritable(0);
    @Override
    protected void map(Object key, Text value,
            Context context)
            throws IOException, InterruptedException {
        // 这里用的inputformat输入格式为KeyValueTextInputFormat  所以会自动将key设置为第一列,第二列为value
        sw.setstrName(key.toString());
        sw.setstrValue(value.toString());
        int valueint=Integer.parseInt(value.toString());
        System.out.println("Map key " +sw.getstrName() +" "+sw.getstrValue()+" value "+valueint);
        intWritable.set(valueint);
        context.write(sw, intWritable);
    }
}

c. Reducer函数

import java.io.IOException;
import java.util.Iterator;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class ReduceStr  extends Reducer<StrWritable, IntWritable, Text, Text>{
    //static{System.out.println("-----------------------------------");}
    @Override
    protected void reduce(StrWritable sw, Iterable<IntWritable> iterable,
            Context context)
            throws IOException, InterruptedException {
        Iterator<IntWritable>  it =iterable.iterator();
        StringBuffer sb = new StringBuffer();
        while(it.hasNext()){
            sb.append(it.next().get()).append(',');
        }
        if(sb.length()>0)
            sb.deleteCharAt(sb.length()-1);
        //System.out.println(sw.getstrName() +"   "+sb.toString());
        context.write(new Text(sw.getstrValue()), new Text(sb.toString()));
    }
}

d. StrNameComparator 分组函数

import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
/*关于实现的是降序还是升序问题,进行比较看返回值,返回1为升序 -1位降序
 * */
public class StrNameComparator extends  WritableComparator{
    public StrNameComparator(){
        super(StrWritable.class,true);
    }
    @Override
    @SuppressWarnings("rawtypes")
    public int compare(WritableComparable a, WritableComparable b) {
        StrWritable s1 = (StrWritable)a;
        StrWritable s2 =(StrWritable)b;
        return s1.getstrName().compareTo(s2.getstrName());
    }
}

e. SortComparator 二次排序函数

import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;

public class SortComparator extends  WritableComparator{
    public SortComparator(){
        super(StrWritable.class,true);
    }
    @Override
    @SuppressWarnings("rawtypes")
    public int compare(WritableComparable a,  WritableComparable b) {
        StrWritable s1 = (StrWritable)a;
        StrWritable s2 =(StrWritable)b;
        int i = s1.getstrName().compareTo(s2.getstrName());
        if(i!=0)
            return i;
        return s1.getstrValue().compareTo(s2.getstrValue());
    }

f. Job主方法

import java.io.IOException;
import java.net.URI;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;
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.input.KeyValueTextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class JobStr {

    /**
     * @param args
     * @throws IOException 
     * @throws InterruptedException 
     * @throws ClassNotFoundException 
     */
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        if(args.length!=2){
            System.err.println("输入正确的参数");
            System.exit(2);
        }

        Configuration conf = new  Configuration();

        Job job = Job.getInstance(conf,"JobStr Dino");
        job.setJarByClass(JobStr.class);

        job.setMapperClass(MapStr.class);
        job.setInputFormatClass(KeyValueTextInputFormat.class);


        job.setReducerClass(ReduceStr.class);
        job.setMapOutputKeyClass(StrWritable.class);
        job.setMapOutputValueClass(IntWritable.class);

        job.setSortComparatorClass(SortComparator.class);
        job.setGroupingComparatorClass(StrNameComparator.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);
        //判断输入路径是否存在,存在即删除
                FileSystem fs= FileSystem.get(URI.create(args[1]), conf);
                Path path  = new Path(args[1]);
                if(fs.exists(path))
                {   try 
                    {
                        fs.delete(path, true);
                        System.out.println(path.getName() + "删除成功 ");
                    }catch(Exception e){
                        System.err.println(e.getMessage());
                    }
                }

        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

4、总结

在这里尽量把我自己总结的东西分享出来,未必能让大家懂甚至也可能存在一些不妥的地方,但都希望能被大家借鉴到一些;在这总结中也存在知识深度的不够,比如partitioner、排序带来的是升序还是降序该怎么控制等等,由于是刚开始写博客,对markdown不太会用,以后都慢慢改进,请大家将就。
欢迎大家一起交流。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值