1、数据样本,w1.csv到w5.csv,每个文件数据样本2000条,第一列是年份从1990到2000随机,第二列数据从1-100随机,本例辅助排序目标是找出每年最大值,实际上结果每年最大就是100,但是这里通过mapreduce辅助排序方式来找。

1999,71
1994,57
1995,33
1993,44
1994,99
1994,83
1995,59
... ...

2、核心概念:

1)分区,假设有海量的数据,为了增加并行度,按照hash算法将所有数据分区后,确保同一年的数据进入到同一个分区,也就是同一个reduce里。

2)键比较器,将数据拆分后,年份和数据同时组成一个组合键(本质就是一个对象),因为采用组合键的key,需要一个键排序比较器来对key排序,通过年份升序,数据降序的算法编写核心比较方法。
那么mapper就会安装key比较器将自己所负责的所有数据排序。 3)分组比较器,在reducer阶段,需求实际上只求最大值,那么实际上就是排序后的第一条,如果reducer阶段不做什么变化,那么数据将会安装年份升序和数据降序输出所有数据(重复的已经被reduce过滤)。
为了只得到一条最大的数据,可以采用设置分组器的方式实现。同一年份,我们只需要一条数据,那么分组比较器就可以只按照年份分组,分组后,reducer最终归并数据后,只会得到排第一的那条最大数据。
这种取最大值得方式,实际上是取巧,是依赖mapper和reducer的排序特性而来。

3、IntPair,本例把整行数据解析后,年份和数据都放入key,需要自定义一个IntPair对象,实际生产环境中可根据需求自定义各种类.

public class IntPair implements WritableComparable<IntPair> {
private int first;
private int second; public IntPair() {
} public IntPair(int first, int second) {
this.first = first;
this.second = second;
} public int getFirst() {
return first;
} public void setFirst(int first) {
this.first = first;
} public int getSecond() {
return second;
} public void setSecond(int second) {
this.second = second;
} @Override
public int compareTo(IntPair o) {
int result = Integer.valueOf(first).compareTo(o.getFirst());
if(result==0){
result = Integer.valueOf(second).compareTo(o.getSecond());
}
return result;
} public static int compare(int first1,int first2){
return Integer.valueOf(first1).compareTo(Integer.valueOf(first2));
} @Override
public void write(DataOutput out) throws IOException {
out.writeInt(first);
out.writeInt(second);
} @Override
public void readFields(DataInput in) throws IOException {
first = in.readInt();
second = in.readInt();
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; IntPair intPair = (IntPair) o; if (first != intPair.first) return false;
return second == intPair.second;
} @Override
public int hashCode() {
int result = first;
result = 31 * result + second;
return result;
} @Override
public String toString() {
return first+"\t"+second;
}
}

4、RecordParser,记录解析器,用于解析数据,规避错误数据

public class RecordParser {
private int year;
private int data;
private boolean valid; public int getYear() {
return year;
} public int getData() {
return data;
} public boolean isValid() {
return valid;
} public void parse(String value){
String[] sValue = value.split(",");
try {
year = Integer.parseInt(sValue[0]);
data = Integer.parseInt(sValue[1]);
valid = true;
}catch (Exception e){
valid = false;
}
}
}

5、分区器

/**
* @Author: xu.dm
* @Date: 2019/2/21 11:56
* @Description:根据key进行分区,确保同一个key.first进入相同的分区,泛型类型和mapper输出一致
*/
public class FirstPartitioner extends Partitioner<IntPair,IntWritable> {
/**
* Get the partition number for a given key (hence record) given the total
* number of partitions i.e. number of reduce-tasks for the job.
* <p>
* <p>Typically a hash function on a all or a subset of the key.</p>
*
* @param key the key to be partioned.
* @param value the entry value.
* @param numPartitions the total number of partitions.
* @return the partition number for the <code>key</code>.
*/
@Override
public int getPartition(IntPair key, IntWritable value, int numPartitions) {
return Math.abs(key.getFirst() * 127) % numPartitions;
}
}

6、key比较器,map阶段的key排序使用,如果没有分组比较器,则key比较器也会应用在混洗和reduce阶段。

/**
* @Author: xu.dm
* @Date: 2019/2/21 11:59
* @Description: key比较器
* 对IntPair的first升序,second降序,在mapper排序的时候被应用
* 最终同样年份的数据第一条是最大的。
*/
public class KeyComparator extends WritableComparator {
protected KeyComparator() {
super(IntPair.class,true);//需要实例化
} @Override
public int compare(WritableComparable a, WritableComparable b) {
IntPair p1=(IntPair)a;
IntPair p2=(IntPair)b;
int result = IntPair.compare(p1.getFirst(),p2.getFirst());
if(result==0){
result = -IntPair.compare(p1.getSecond(),p2.getSecond()); //前面加一个减号求反
}
return result;
}
}

7、分组比较器,这里最关键,看注释。

/**
* @Author: xu.dm
* @Date: 2019/2/21 12:16
* @Description: 分组比较器,应用在reduce阶段,数据进reduce后,归并之前。
* 本例目标是:确保同一个年份的数据在同一个组里
* 之前key比较器使得key值中的年份升序,数据降序排列。
* 那么这个分组比较器只按年进行比较,意味着,[1990,100]和[1990,00]会被认为是相同的分组,
* 而,reduce阶段,相同的KEY只取第一个,哦也,这个时候,reduce阶段后,年份中最大的数据就被保存下来,其他数据都被kickout
* 所以,用这种方式变相的达到取最大值得效果。
*/
public class GroupComparator extends WritableComparator {
public GroupComparator() {
super(IntPair.class,true);
} @Override
public int compare(WritableComparable a, WritableComparable b) {
IntPair p1=(IntPair)a;
IntPair p2=(IntPair)b;
return IntPair.compare(p1.getFirst(),p2.getFirst());
}
}

8、mapper,如果只取年份里的最大数据,Mapper<LongWritable,Text,IntPair,IntWritable> 的IntWritable可以用NullWritable,这里保留IntWritable是因为,程序稍加改动就可以输出所有年份数据的计数

public class DataMapper extends Mapper<LongWritable,Text,IntPair,IntWritable> {
private RecordParser parser = new RecordParser(); @Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
parser.parse(value.toString());
if(parser.isValid()){
context.write(new IntPair(parser.getYear(),parser.getData()),new IntWritable(1));
context.getCounter("MapValidData","dataCounter").increment(1); //做一个计数,总的数据应该是10000条。
}
}
}

9、reducer

public class DataReducer extends Reducer<IntPair,IntWritable,IntPair,IntWritable> {

    @Override
protected void reduce(IntPair key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
//因为分组器,[1990,100]和[1990,00]会被认为是相同的分组
//这里的计数就会混淆。如果需要年份下各数据的正确的计数结果,则需要注销分组器
// for(IntWritable val:values){
// sum+=val.get();
// } context.write(key,new IntWritable(sum));
}
}

10、job

public class DataSecondarySort extends Configured implements Tool {
/**
* Execute the command with the given arguments.
*
* @param args command specific arguments.
* @return exit code.
* @throws Exception
*/
@Override
public int run(String[] args) throws Exception {
Configuration conf = getConf(); Job job = Job.getInstance(conf,"Secondary Sort");
// conf.set("mapreduce.job.ubertask.enable","true"); if(conf==null){
return -1;
} job.setJarByClass(DataSecondarySort.class);
job.setMapperClass(DataMapper.class);
job.setPartitionerClass(FirstPartitioner.class);
job.setSortComparatorClass(KeyComparator.class);
// 决定如何分组
job.setGroupingComparatorClass(GroupComparator.class);
job.setReducerClass(DataReducer.class);
// job.setNumReduceTasks(2);//如果数据海量,则可以根据情况设置reduce的数目,也是分区的数量,通过Tool类,也可以在命令行进行设置 job.setOutputKeyClass(IntPair.class);
//如果只求最大数,前面的mapper,reducer和这里的输出都可以设置成NullWritable
job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1])); Path outPath = new Path(args[1]);
FileSystem fileSystem = outPath.getFileSystem(conf);
//删除输出路径
if(fileSystem.exists(outPath))
{
fileSystem.delete(outPath,true);
} return job.waitForCompletion(true) ? 0:1;
} public static void main(String[] args) throws Exception{
int exitCode = ToolRunner.run(new DataSecondarySort(),args);
System.exit(exitCode);
}
}

11、如果求最大值,结果会是这样:

[hadoop@bigdata-senior01 ~]$ hadoop fs -cat /output3/part-r-00000 | more
1990 100 0
1991 100 0
1992 100 0
1993 100 0
1994 100 0
1995 100 0
1996 100 0
1997 100 0
1998 100 0
1999 100 0
2000 100 0

如果求最大值和计数则会列出所有数据,当然需要注销分组器的set代码,并打开reducer的sum

[hadoop@bigdata-senior01 ~]$ hadoop fs -cat /output/part-r-00000 | more
1990 100 10
1990 99 15
1990 98 10
1990 97 9
1990 96 6
1990 95 4
1990 94 12
1990 93 9
1990 92 12
1990 91 13
1990 90 8
1990 89 9
... ...

多个分区可以使用job.setNumReduceTasks(n),或者在命令行上指定

[hadoop@bigdata-senior01 ~]$ hadoop jar DataSecondarySort.jar -D mapreduce.job.reduces=3 /sampler /output3

[hadoop@bigdata-senior01 ~]$ hadoop fs -ls /output3
Found 4 items
-rw-r--r-- 1 hadoop supergroup 0 2019-02-21 15:29 /output3/_SUCCESS
-rw-r--r-- 1 hadoop supergroup 2868 2019-02-21 15:29 /output3/part-r-00000
-rw-r--r-- 1 hadoop supergroup 3860 2019-02-21 15:29 /output3/part-r-00001
-rw-r--r-- 1 hadoop supergroup 3850 2019-02-21 15:29 /output3/part-r-00002

总结一下:最容易混淆的概念就是分组,而排序和分组实际上就是MapReduce最核心的地方。

总结步骤:
1、视数据量大小决定是否分区,分几个区输出数据,可以在作业中设置,也可以在命令行中指定
2、规划数据结构,抽象为对象,自定义对象排序规则,实现排序接口,明确key排序比较器
3、自定义分组规则,视情况进行分组归纳,实现分组排序接口
4、作业配置中配置分区类、排序类、分组类以及输出类。
 

hadoop MapReduce辅助排序解析的更多相关文章

  1. 三种方法实现Hadoop(MapReduce)全局排序(1)

    我们可能会有些需求要求MapReduce的输出全局有序,这里说的有序是指Key全局有序.但是我们知道,MapReduce默认只是保证同一个分区内的Key是有序的,但是不保证全局有序.基于此,本文提供三 ...

  2. MapReduce辅助排序

    需求:订单数据 求出每个订单中最贵的商品? 订单id正序,成交金额倒序. 结果文件三个,每个结果文件只要一条数据. 1.Mapper类 package com.css.order.mr; import ...

  3. Hadoop mapreduce自定义排序WritableComparable

    本文发表于本人博客. 今天继续写练习题,上次对分区稍微理解了一下,那根据那个步骤分区.排序.分组.规约来的话,今天应该是要写个排序有关的例子了,那好现在就开始! 说到排序我们可以查看下hadoop源码 ...

  4. Hadoop mapreduce自定义分组RawComparator

    本文发表于本人博客. 今天接着上次[Hadoop mapreduce自定义排序WritableComparable]文章写,按照顺序那么这次应该是讲解自定义分组如何实现,关于操作顺序在这里不多说了,需 ...

  5. Hadoop Mapreduce分区、分组、二次排序过程详解[转]

    原文地址:Hadoop Mapreduce分区.分组.二次排序过程详解[转]作者: 徐海蛟 教学用途 1.MapReduce中数据流动   (1)最简单的过程:  map - reduce   (2) ...

  6. Hadoop Mapreduce分区、分组、二次排序

    1.MapReduce中数据流动   (1)最简单的过程:  map - reduce   (2)定制了partitioner以将map的结果送往指定reducer的过程: map - partiti ...

  7. Hadoop案例(八)辅助排序和二次排序案例(GroupingComparator)

    辅助排序和二次排序案例(GroupingComparator) 1.需求 有如下订单数据 订单id 商品id 成交金额 0000001 Pdt_01 222.8 0000001 Pdt_05 25.8 ...

  8. Hadoop Mapreduce分区、分组、二次排序过程详解

    转载:http://blog.tianya.cn/m/post.jsp?postId=53271442 1.MapReduce中数据流动 (1)最简单的过程:  map - reduce (2)定制了 ...

  9. Hadoop MapReduce编程 API入门系列之自定义多种输入格式数据类型和排序多种输出格式(十一)

    推荐 MapReduce分析明星微博数据 http://git.oschina.net/ljc520313/codeexample/tree/master/bigdata/hadoop/mapredu ...

随机推荐

  1. HIS系统两种收费模式比较:前计费和后计费

    一.药品 a.前计费:审核(临时医嘱)或者分解(长期医嘱)计费 退费处理方式,1)如果是还未发药,则护士站直接退费;2)如果药房已经发药,则护士站发出退费申请,由护士拿着药品去药房退药退费. b.后计 ...

  2. android 学习四 ContentProvider

    1.系统自带的许多数据(联系人,本地信息等)保存在sqllite数据库,然后封装成许多ContentProvider来供其他程序访问. 2.对sqllite数据库的操作,可以在命令行通过adb工具登录 ...

  3. 「日常训练」湫湫系列故事——设计风景线(HDU-4514)

    题意与分析 中文题目,木得题意的讲解谢谢. 然后还是分解成两个任务:a)判环,b)找最长边. 对于这样一个无向图,强行转换成负权然后bellman-ford算法求最短是难以实现的,所以感谢没有环--我 ...

  4. 使用gitlab时候 fork仓库不会实时从主仓库更新解决方案

    付费用户可以使用现成的方案,地址见 链接 但是私有gitlab时候,需要手动进行如下操作 1. Clone your fork: git clone git@github.com:YOUR-USERN ...

  5. 第5章 Linux网络编程基础

    第5章 Linux网络编程基础 5.1 socket地址与API 一.理解字节序 主机字节序一般为小端字节序.网络字节序一般为大端字节序.当格式化的数据在两台使用了不同字节序的主机之间直接传递时,接收 ...

  6. 前端开发工程师 - 01.页面制作 - 第1章.Photoshop切图

    第1章--Photoshop切图 工具.面板.视图 什么是切图? 1. 从设计稿(.psd)中切出网络素材,如按钮.图标.logo.背景图等 2. 编写代码,在代码中使用图片,生成静态页面 --给网页 ...

  7. Linux文件系统简介和软链接和硬链接的区别

    Linux有着极其丰富的文件系统,大体可分为如下几类: 网络文件系统:如nfs.cifs等: 磁盘文件系统:如ext3.ext4等: 特殊文件系统:如prco.sysfs.ramfs.tmpfs等: ...

  8. [Clr via C#读书笔记]Cp15枚举和位标识

    Cp15枚举和位标识 枚举类型 本质是结构,符号名称-值:好处显而易见:System.Enum;值类型: 编译的时候,符号会转换为常量字段: 枚举支持很多方法和成员: 位标识bit flag 判断和设 ...

  9. Sharepoint 2013与Sharepoint 2016的功能对比

    开发人员功能 SharePoint Foundation 2013 SharePoint Server 2013 Standard CAL SharePoint Server 2013 Enterpr ...

  10. dice2win早期版本

    原理; https://medium.com/dapppub/fairdicedesign-315a4e253ad6 早期版本地址: https://etherscan.io/address/0xD1 ...