MapReduce的自制Writable分组输出及组内排序

  • 问题描述:

    输入文件格式如下:

    name1    2

    name3    4

    name1    6

    name1    1

    name3    3

    name1    0

    要求输出的文件格式如下:

    name1    0,1,2,6

    name3    3,4

    要求是按照第一列分组,name1与name3也是按照顺序排列的,组内升序排序

    思路:

    常规的输出,无法排序key所对应的多个值的顺序。为了排序组内中的值,需要将key与value放在同一个组。Job中有两个方法setGroupingComparatorClass和setSortComparatorClass,可以利用这两个方法来实现组内排序。但是这些排序都是基于key的,则就要将key和value定义成组合键。

    但是必须要保证第一列相同的全部都放在同一个分区中,则就需要自定义分区,分区的时候只考虑第一列的值。由于partitioner仅仅能保证每一个reducer接受同一个name的所有记录,但是reducer仍然是通过键进行分组的分区,也就说该分区中还是按照键来分成不同的组,还需要分组只参考name值

    先按照name分组,再在name中内部进行排序。

    解决方法:

    运用自定义组合键的策略,将name和1定义为一个组合键。在分区的时候只参考name的值,即继承partitioner。

     由于要按照name分组,则就需要定义分组策略,然后设置setGroupingComparatorClass。

    setGroupingComparatorClass主要定义哪些key可以放置在一组,分组的时候会对组合键进行比较,由于这里只需要考虑组合键中的一个值,则定义实现一个WritableComparator,设置比较策略。

    对于组内的排序,可以利用setSortComparatorClass来实现,

    这个方法主要用于定义key如何进行排序在它们传递给reducer之前,

    这里就可以来进行组内排序。

    具体代码:

         Hadoop版本号:hadoop1.1.2

    自定义组合键


    01. package whut;
    02. import java.io.DataInput;
    03. import java.io.DataOutput;
    04. import java.io.IOException;
    05. import org.apache.hadoop.io.IntWritable;
    06. import org.apache.hadoop.io.Text;
    07. import org.apache.hadoop.io.WritableComparable;
    08. //自定义组合键策略
    09. //java基本类型数据
    10. public class TextInt implements WritableComparable{
    11. //直接利用java的基本数据类型
    12. private String firstKey;
    13. private int secondKey;
    14. //必须要有一个默认的构造函数
    15. public String getFirstKey() {
    16. return firstKey;
    17. }
    18. public void setFirstKey(String firstKey) {
    19. this.firstKey = firstKey;
    20. }
    21. public int getSecondKey() {
    22. return secondKey;
    23. }
    24. public void setSecondKey(int secondKey) {
    25. this.secondKey = secondKey;
    26. }
    27.  
    28. @Override
    29. public void write(DataOutput out) throws IOException {
    30. // TODO Auto-generated method stub
    31. out.writeUTF(firstKey);
    32. out.writeInt(secondKey);
    33. }
    34. @Override
    35. public void readFields(DataInput in) throws IOException {
    36. // TODO Auto-generated method stub
    37. firstKey=in.readUTF();
    38. secondKey=in.readInt();
    39. }
    40. //map的键的比较就是根据这个方法来进行的
    41. @Override
    42. public int compareTo(Object o) {
    43. // TODO Auto-generated method stub
    44. TextInt ti=(TextInt)o;
    45. //利用这个来控制升序或降序
    46. //this本对象写在前面代表是升序
    47. //this本对象写在后面代表是降序
    48. return this.getFirstKey().compareTo(ti.getFirstKey());
    49. }
    50. }

    分组策略

     

    01. package whut;
    02. import org.apache.hadoop.io.WritableComparable;
    03. import org.apache.hadoop.io.WritableComparator;
    04. //主要就是对于分组进行排序,分组只按照组建键中的一个值进行分组
    05. public class TextComparator extends WritableComparator {
    06. //必须要调用父类的构造器
    07. protected TextComparator() {
    08. super(TextInt.class,true);//注册comparator
    09. }
    10. @Override
    11. public int compare(WritableComparable a, WritableComparable b) {
    12. // TODO Auto-generated method stub
    13. TextInt ti1=(TextInt)a;
    14. TextInt ti2=(TextInt)b;
    15. return ti1.getFirstKey().compareTo(ti2.getFirstKey());
    16. }
    17. }

    组内排序策略

     

    01. package whut;
    02. import org.apache.hadoop.io.WritableComparable;
    03. import org.apache.hadoop.io.WritableComparator;
    04. //分组内部进行排序,按照第二个字段进行排序
    05. public class TextIntComparator extends WritableComparator {
    06. public TextIntComparator()
    07. {
    08. super(TextInt.class,true);
    09. }
    10. //这里可以进行排序的方式管理
    11. //必须保证是同一个分组的
    12. //a与b进行比较
    13. //如果a在前b在后,则会产生升序
    14. //如果a在后b在前,则会产生降序
    15. @Override
    16. public int compare(WritableComparable a, WritableComparable b) {
    17. // TODO Auto-generated method stub
    18. TextInt ti1=(TextInt)a;
    19. TextInt ti2=(TextInt)b;
    20. //首先要保证是同一个组内,同一个组的标识就是第一个字段相同
    21. if(!ti1.getFirstKey().equals(ti2.getFirstKey()))
    22. return ti1.getFirstKey().compareTo(ti2.getFirstKey());
    23. else
    24. return ti2.getSecondKey()-ti1.getSecondKey();//0,-1,1
    25. }
    26.  
    27. }

    分区策略

     

    01. package whut;
    02. import org.apache.hadoop.io.IntWritable;
    03. import org.apache.hadoop.mapreduce.Partitioner;
    04. //参数为map的输出类型
    05. public class KeyPartitioner extends Partitioner<TextInt, IntWritable> {
    06. @Override
    07. public int getPartition(TextInt key, IntWritable value, int numPartitions) {
    08. // TODO Auto-generated method stub
    09. return (key.getFirstKey().hashCode()&Integer.MAX_VALUE)%numPartitions;
    10. }
    11. }

    MapReduce策略

     

    001. package whut;
    002. import java.io.IOException;
    003. import org.apache.hadoop.conf.Configuration;
    004. import org.apache.hadoop.conf.Configured;
    005. import org.apache.hadoop.fs.Path;
    006. import org.apache.hadoop.io.IntWritable;
    007. import org.apache.hadoop.io.Text;
    008. import org.apache.hadoop.mapreduce.Job;
    009. import org.apache.hadoop.mapreduce.Mapper;
    010. import org.apache.hadoop.mapreduce.Reducer;
    011. import org.apache.hadoop.mapreduce.Mapper.Context;
    012. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
    013. import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;
    014. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
    015. import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
    016. import org.apache.hadoop.util.Tool;
    017. import org.apache.hadoop.util.ToolRunner;
    018. //需要对数据进行分组以及组内排序的时候
    019. public class SortMain extends Configured implements Tool{
    020. //这里设置输入文格式为KeyValueTextInputFormat
    021. //name1 5
    022. //默认输入格式都是Text,Text
    023. public static class GroupMapper extends
    024. Mapper<Text, Text, TextInt, IntWritable>  {
    025. public IntWritable second=new IntWritable();
    026. public TextInt tx=new TextInt();
    027. @Override
    028. protected void map(Text key, Text value, Context context)
    029. throws IOException, InterruptedException {
    030. String lineKey=key.toString();
    031. String lineValue=value.toString();
    032. int lineInt=Integer.parseInt(lineValue);
    033. tx.setFirstKey(lineKey);
    034. tx.setSecondKey(lineInt);
    035. second.set(lineInt);
    036. context.write(tx, second);
    037. }
    038. }
    039. //设置reduce
    040. public static class GroupReduce extends Reducer<TextInt, IntWritable, Text, Text>
    041. {
    042. @Override
    043. protected void reduce(TextInt key, Iterable<IntWritable> values,
    044. Context context)
    045. throws IOException, InterruptedException {
    046. StringBuffer sb=new StringBuffer();
    047. for(IntWritable val:values)
    048. {
    049. sb.append(val+",");
    050. }
    051. if(sb.length()>0)
    052. {
    053. sb.deleteCharAt(sb.length()-1);
    054. }
    055. context.write(new Text(key.getFirstKey()), new Text(sb.toString()));
    056. }
    057. }
    058.  
    059. @Override
    060. public int run(String[] args) throws Exception {
    061. // TODO Auto-generated method stub
    062. Configuration conf=getConf();
    063. Job job=new Job(conf,"SecondarySort");
    064. job.setJarByClass(SortMain.class);
    065. // 设置输入文件的路径,已经上传在HDFS
    066. FileInputFormat.addInputPath(job, new Path(args[0]));
    067. // 设置输出文件的路径,输出文件也存在HDFS中,但是输出目录不能已经存在
    068. FileOutputFormat.setOutputPath(job, new Path(args[1]));
    069.  
    070. job.setMapperClass(GroupMapper.class);
    071. job.setReducerClass(GroupReduce.class);
    072. //设置分区方法
    073. job.setPartitionerClass(KeyPartitioner.class);
    074.  
    075. //下面这两个都是针对map端的
    076. //设置分组的策略,哪些key可以放置到一组中
    077. job.setGroupingComparatorClass(TextComparator.class);
    078. //设置key如何进行排序在传递给reducer之前.
    079. //这里就可以设置对组内如何排序的方法
    080. /*************关键点**********/
    081. job.setSortComparatorClass(TextIntComparator.class);
    082. //设置输入文件格式
    083. job.setInputFormatClass(KeyValueTextInputFormat.class);
    084. //使用默认的输出格式即TextInputFormat
    085. //设置map的输出key和value类型
    086. job.setMapOutputKeyClass(TextInt.class);
    087. job.setMapOutputValueClass(IntWritable.class);
    088. //设置reduce的输出key和value类型
    089. //job.setOutputFormatClass(TextOutputFormat.class);
    090. job.setOutputKeyClass(Text.class);
    091. job.setOutputValueClass(Text.class);
    092. job.waitForCompletion(true);
    093. int exitCode=job.isSuccessful()?0:1;
    094. return exitCode;
    095. }
    096.  
    097. public static void main(String[] args)  throws Exception
    098. {
    099. int exitCode=ToolRunner.run(new SortMain(), args);
    100. System.exit(exitCode);
    101. }
    102. }

    注意事项

       1,设置分组排序按照升序还是降序是在自定义WritableComparable中的compareTo()方法实现的,具体升序或者降序的设置在代码中已经注释说明

       2,设置组内值进行升序还是降序的排序是在组内排序策略中的compare()方法注释说明的。

       3,这里同时最重要的一点是,将第二列即放在组合键中,又作为value,这样对于组合键排序也就相当于对于value进行排序了。

       4,在自定义组合键的时候,对于组合键中的数据的基本类型可以采用Java的基本类型也可以采用Hadoop的基本数据类型,对于Hadoop的基本数据类型一定要记得初始化new一个基本数据类型对象。对于组合键类,必须要有默认的构造方法。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值