MapReduce 二次排序:对表格数据进行多字段排序

MapReduce 在传递 <key, value> 对时默认根据 key 进行排序,所以在对表格数据按某一字段进行排序时可以设置排序的字段为 key。而有时候我们需要对两个字段分别进行排序,基于这种需求进行的自定义排序称为'二次排序'。

例如有以下数据:

| first_name | last_name | age |
|---|---|---| | A | Smith | 25 | | B | Jones | 30 | | C | Brown | 22 | | B | White | 28 | | A | Black | 27 | | C | Green | 29 | | A | Davis | 24 |

如果对上面的数据按第一个字段值进行升序排列,那么有些数据的顺序是相同的,这时候我们就要按照第二个字段值再进行一次排序,例如:先按照第一个值(字母)升序排列,再按照第二个值(数字) 进行降序排列得到:

A, 3 B, 5 C, 1 B, 6 A, 4 C, 5 A, 4 A, 3 B, 6 B, 5 C, 5 C, 1

要求:编写一个 Java 程序,利用 MapReduce 技术对数据进行二次排序,把程序打包成 jar 文件运行得到结果。

**(1)**在程序中读取给定的表格文件 datas.xlsx 的内容(表格的第一行为标题,处理时需要排除),首先对 first_name 按首字母(不区分大小写)进行分区,首字母相同的行在同一分区(即输出在同一文件,如 A、a 开头的行在第 1 个分区 part-r-00000,Z、z 开头的行在第 26 个分区 part-r-00025)

**(2)**然后在每个分区内对数据进行二次排序,要求先对 first_name 字段降序排列,再对 last_name 字段也进行降序排列,最后 MapReduce 输出的每一行结果中,first_name 和 last_name 之间用空格分隔,其他字段用英文分号分隔

内容:由于缺少数据,下面的代码是一个基本的二次排序的示例,实现了按照第一个字段升序排序,第二个字段降序排序的功能。

Mapper:

public class SortMapper extends Mapper<LongWritable, Text, MyKey, Text> {

    private MyKey keyInfo = new MyKey();
    private Text valueInfo = new Text();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String[] fields = value.toString().split(',');
        String first = fields[0];
        int second = Integer.parseInt(fields[1]);
        keyInfo.setFirst(first);
        keyInfo.setSecond(second);
        valueInfo.set(fields[1]);
        context.write(keyInfo, valueInfo);
    }
}

Reducer:

public class SortReducer extends Reducer<MyKey, Text, Text, Text> {

    private Text outputKey = new Text();
    private Text outputValue = new Text();

    @Override
    protected void reduce(MyKey key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
        for (Text value : values) {
            outputKey.set(key.getFirst());
            outputValue.set(key.getSecond() + '');
            context.write(outputKey, outputValue);
        }
    }
}

MyKey:

public class MyKey implements WritableComparable<MyKey> {

    private String first;
    private int second;

    public MyKey() {
    }

    public MyKey(String first, int second) {
        this.first = first;
        this.second = second;
    }

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }

    public int getSecond() {
        return second;
    }

    public void setSecond(int second) {
        this.second = second;
    }

    @Override
    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeUTF(first);
        dataOutput.writeInt(second);
    }

    @Override
    public void readFields(DataInput dataInput) throws IOException {
        first = dataInput.readUTF();
        second = dataInput.readInt();
    }

    @Override
    public int compareTo(MyKey o) {
        int cmp = first.compareTo(o.getFirst());
        if (cmp != 0) {
            return cmp;
        }
        return o.getSecond() - second;
    }
}

Driver:

public class SortDriver {

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, 'secondary sort');
        job.setJarByClass(SortDriver.class);
        job.setMapperClass(SortMapper.class);
        job.setReducerClass(SortReducer.class);
        job.setOutputKeyClass(MyKey.class);
        job.setOutputValueClass(Text.class);
        job.setPartitionerClass(FirstCharPartitioner.class);
        job.setGroupingComparatorClass(FirstCharGroupingComparator.class);
        job.setSortComparatorClass(MyKeyComparator.class);
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

FirstCharPartitioner:

public class FirstCharPartitioner extends Partitioner<MyKey, Text> {

    @Override
    public int getPartition(MyKey key, Text value, int numPartitions) {
        return key.getFirst().toLowerCase().charAt(0) - 'a';
    }
}

FirstCharGroupingComparator:

public class FirstCharGroupingComparator extends WritableComparator {

    protected FirstCharGroupingComparator() {
        super(MyKey.class, true);
    }

    @Override
    public int compare(WritableComparable a, WritableComparable b) {
        MyKey key1 = (MyKey) a;
        MyKey key2 = (MyKey) b;
        return key1.getFirst().compareToIgnoreCase(key2.getFirst());
    }
}

MyKeyComparator:

public class MyKeyComparator extends WritableComparator {

    protected MyKeyComparator() {
        super(MyKey.class, true);
    }

    @Override
    public int compare(WritableComparable a, WritableComparable b) {
        MyKey key1 = (MyKey) a;
        MyKey key2 = (MyKey) b;
        return key1.compareTo(key2);
    }
}

代码说明:

  • MyKey: 自定义的 key 类,包含了两个排序字段 first 和 second,实现了 Comparable 接口,用于定义排序规则。
  • SortMapper: 将输入数据解析成 <MyKey, Text> 对,并将数据写入 context。
  • SortReducer: 将相同 key 的数据进行二次排序,并输出排序后的结果。
  • FirstCharPartitioner: 将数据根据第一个字段的首字母进行分区。
  • FirstCharGroupingComparator: 在每个分区内对数据进行分组,并将相同第一个字段的值进行比较。
  • MyKeyComparator: 对 MyKey 对象进行排序。

注意:

  • 本代码仅供参考,需要根据实际情况进行调整。
  • 需要添加依赖库:hadoop-core、commons-logging、commons-io 等。
  • 可以使用 Maven 或其他工具构建项目。
  • 需要将代码打包成 jar 文件,并使用 Hadoop 命令运行。
MapReduce 二次排序:对表格数据进行多字段排序

原文地址: https://www.cveoy.top/t/topic/oI4a 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录