1.日志源文件 (各个列分别是: 账户,营业额,花费,日期)

zhangsan@163.com    6000    0    2014-02-20
lisi@163.com 2000 0 2014-02-20
lisi@163.com 0 100 2014-02-20
zhangsan@163.com 3000 0 2014-02-20
wangwu@126.com 9000 0 2014-02-20
wangwu@126.com 0 200 2014-02-20

想要的结果: (计算出每个账户的总营业额和总花费,要求营业额排序降序,如果营业额相同则花费少的在上面)

zhangsan@163.com    9000    0    9000
wangwu@126.com 9000 200 8800
lisi@163.com 2000 100 1900

2.写代码:

InfoBean.java  对账户的后三个字段封装成一个Bean对象

 import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException; import org.apache.hadoop.io.WritableComparable; //要和其他的InfoBean类型进行比较,所以此处泛型T为InfoBean
public class InfoBean implements WritableComparable<InfoBean> { private String account;
private double income;
private double expenses;
private double surplus; /*
*如果不写这个方法,封装InfoBean对象的时候就要分别set这个对象的各个属性.
*/
public void set(String account,double income,double expenses){
this.account = account;
this.income = income;
this.expenses = expenses;
this.surplus = income -expenses;
}
@Override
public void write(DataOutput out) throws IOException {
out.writeUTF(account);
out.writeDouble(income);
out.writeDouble(expenses);
out.writeDouble(surplus);
} @Override
public void readFields(DataInput in) throws IOException {
this.account = in.readUTF();
this.income = in.readDouble();
this.expenses = in.readDouble();
this.surplus = in.readDouble();
} @Override
public int compareTo(InfoBean o) {
if(this.income == o.getIncome()){
return this.expenses > o.getExpenses() ? 1 : -1;
} else {
return this.income > o.getIncome() ? -1 : 1;
}
} @Override
//toString()方法输出的格式最好和源文件trade_info.txt中的格式一样, 字段通过Tab键分隔.
//而且在SumReducer类输出k3,v3的时候会输出k3(context.write(key, v);) 所以这个地方没有必要再输出k3(account)
public String toString() {
// return "InfoBean [account=" + account + ", income=" + income
// + ", expenses=" + expenses + ", surplus=" + surplus + "]";
return this.income + "\t" + this.expenses+"\t" + this.surplus;
}
public double getIncome() {
return income;
} public void setIncome(double income) {
this.income = income;
} public double getExpenses() {
return expenses;
} public void setExpenses(double expenses) {
this.expenses = expenses;
} public double getSurplus() {
return surplus;
} public void setSurplus(double surplus) {
this.surplus = surplus;
} public String getAccount() {
return account;
} public void setAccount(String account) {
this.account = account;
} }

SumStep.java

 import java.io.IOException;

 import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class SumStep { public static class SumMapper extends Mapper<LongWritable, Text, Text, InfoBean>{
private Text k = new Text();
private InfoBean bean = new InfoBean(); @Override
protected void map(LongWritable key, Text value,Mapper<LongWritable, Text, Text, InfoBean>.Context context)
throws IOException, InterruptedException { String line = value.toString();
String [] fields = line.split("\t");
String account = fields[0];
double income = Double.parseDouble(fields[1]);
double expenses = Double.parseDouble(fields[2]);
k.set(account);
bean.set(account, income, expenses);
context.write(k, bean);
}
}
public static class SumReducer extends Reducer<Text, InfoBean, Text, InfoBean>{
private InfoBean v = new InfoBean();
@Override
protected void reduce(Text key, Iterable<InfoBean> values,Reducer<Text, InfoBean, Text, InfoBean>.Context context)
throws IOException, InterruptedException {
double sum_in = 0;
double sum_out = 0;
for(InfoBean bean : values){
sum_in += bean.getIncome();
sum_out += bean.getExpenses();
}
/*
* 在crxy的流量统计的案例中 是如下的方式写出k3和v3的 在reduce方法中new这个封装好的对象.
* 但是如果数据量比较大的情况下 是可能会造成内存溢出的.
* TrafficWritable v3 = new TrafficWritable(t1, t2, t3, t4);
* context.write(k2, v3);
*
* 所以建议把这个封装的对象写在"脑袋顶上" 如上所示....private InfoBean v = new InfoBean();
* 但是如果你Java基础比较好的话可能会说 在Java中是引用传递...所以后面的v会覆盖前面的v,造成最后只有最有一个v
* 其实这里是不会产生问题的,因为context.write()方法会直接把v3对应的InfoBean对象序列化.
* 虽然之前对象的引用确实覆盖了,但是之前对象的值等都保存了下来.是可以放在这个类的"脑袋顶上"的.
* 让这个类公用这个InfoBean对象.
*/ v.set(key.toString(),sum_in,sum_out);
context.write(key, v);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(SumStep.class); job.setMapperClass(SumMapper.class);
//以下两行可以在满足一定条件的时候省略掉.
//在满足k2和k3,v2和v3一一对应的时候就可以省略掉. 看SumReducer类所在行的泛型.
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(InfoBean.class); FileInputFormat.setInputPaths(job, new Path(args[0])); job.setReducerClass(SumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(InfoBean.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
}

项目打成jar包放到Linux中,日志源文件上传到HDFS上.运行结果如下:

hadoop jar /root/itcastmr.jar itcastmr.SumStep /user/root/trade_info.txt /tradeout

但是这个结果并没有排序.还是按照账号的字典排序.

以这个MR的输出当做输入对其根据InfoBean对象进行排序.....

上代码SortStep.java:

 import java.io.IOException;

 import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class SortStep {
//这个Mapper读取的HDFS文件是SumStep Reduce计算输出的文件.
public static class SortMapper extends Mapper<LongWritable, Text, InfoBean, NullWritable>{
private InfoBean k = new InfoBean();
@Override
protected void map(LongWritable key,Text value,Mapper<LongWritable, Text, InfoBean, NullWritable>.Context context)
throws IOException, InterruptedException {
String line = value.toString();
String [] fields = line.split("\t");
String account = fields[0];
double income = Double.parseDouble(fields[1]);
double expenses = Double.parseDouble(fields[2]);
k.set(account, income, expenses);
//现在是要求按照InfoBean对象中的规则排序(InfoBean中有compareTo方法)...所以InfoBean对象当做k2...
context.write(k,NullWritable.get());//不能传null,NullWritable.get() 是获得的this对象.
}
}
public static class SortReducer extends Reducer<InfoBean, NullWritable, Text, InfoBean>{
private Text k = new Text();
@Override
protected void reduce(InfoBean bean, Iterable<NullWritable> values,Reducer<InfoBean, NullWritable, Text, InfoBean>.Context context)
throws IOException, InterruptedException {
String account = bean.getAccount();
k.set(account);
context.write(k, bean);
}
} public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(SortStep.class); job.setMapperClass(SortMapper.class);
//以下两行可以在满足一定条件的时候省略掉.
//在满足k2和k3,v2和v3一一对应的时候就可以省略掉. 看SumReducer类所在行的泛型.
job.setMapOutputKeyClass(InfoBean.class);
job.setMapOutputValueClass(NullWritable.class); FileInputFormat.setInputPaths(job, new Path(args[0])); job.setReducerClass(SortReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(InfoBean.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
}

打成jar包,然后运行命令....输入为上面SumStep.java的输出

hadoop jar /root/itcastmr.jar itcastmr.SortStep /tradeout /trade_sort_out

排序之后的结果:

在MapReduce读取输入数据的时候,如果这个文件是以下划线开始的话,那么会不会读取这个文件中的内容...."_SUCCESS"文件就不会读取....

如果想对某个类进行排序,

1.这个类要实现WritableComparable接口,

2.还要重写compareTo方法. 根据自己的业务逻辑自定义排序.

只需要把要排序的类当做k2 就可以了...框架自动排序.

要排序对象的compareTo方法是框架调用的,框架在shuffle这个阶段会调用排序.

shuffle后面会讲,shuffle由很多很多的阶段组成,分区,排序,分组,combiner等等...把这些小的细节都讲完了之后再讲shuffle.

MapReduce对交易日志进行排序的Demo(MR的二次排序)的更多相关文章

  1. (转)MapReduce二次排序

    一.概述 MapReduce框架对处理结果的输出会根据key值进行默认的排序,这个默认排序可以满足一部分需求,但是也是十分有限的.在我们实际的需求当中,往往有要对reduce输出结果进行二次排序的需求 ...

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

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

  3. MapReduce自定义二次排序流程

    每一条记录开始是进入到map函数进行处理,处理完了之后立马就入自定义分区函数中对其进行分区,当所有输入数据经过map函数和分区函数处理完之后,就调用自定义二次排序函数对其进行排序. MapReduce ...

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

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

  5. MapReduce的二次排序

    附录之前总结的一个例子: http://www.cnblogs.com/DreamDrive/p/7398455.html 另外两个有价值的博文: http://www.cnblogs.com/xux ...

  6. 详细讲解MapReduce二次排序过程

    我在15年处理大数据的时候还都是使用MapReduce, 随着时间的推移, 计算工具的发展, 内存越来越便宜, 计算方式也有了极大的改变. 到现在再做大数据开发的好多同学都是直接使用spark, hi ...

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

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

  8. Hadoop学习之自定义二次排序

    一.概述    MapReduce框架对处理结果的输出会根据key值进行默认的排序,这个默认排序可以满足一部分需求,但是也是十分有限的.在我们实际的需求当中,往 往有要对reduce输出结果进行二次排 ...

  9. hadoop 二次排序的一些思考

    先说一下mr的二次排序需求: 假如文件有两列分别为name.score,需求是先按照name排序,name相同按照score排序 数据如下: jx 20 gj 30 jx 10 gj 15 输出结果要 ...

随机推荐

  1. [leetcode]44. Wildcard Matching万能符匹配

    Given an input string (s) and a pattern (p), implement wildcard pattern matching with support for '? ...

  2. maven发布jar包到私服

    1.setting.xml配置 <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi=&quo ...

  3. BZOJ4380 Myjnie / Luogu3592 [POI2015]MYJ-区间DP

    Description 有$n$家洗车店从左往右排成一排,每家店都有一个正整数价格$p[i]$. 有$m$个人要来消费,第$i$个人会驶过第$a[i]$个开始一直到第$b[i]$个洗车店,且会选择这些 ...

  4. [字符串][NOIP2012]Vigenère密码

    Vigenère密码 题目描述 16世纪法国外交家Blaise de Vigenère设计了一种多表密码加密算法——Vigenère密码.Vigenère密码的加密解密算法简单易用,且破译难度比较高, ...

  5. 设计模式学习心得<抽象工厂模式 Abstract Factory>

    抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在抽 ...

  6. Python3实战系列之八(获取印度售后数据项目)

    问题:续接上一篇.说干咱就干呀,勤勤恳恳写程序呀! 目标:此篇开始进入正题了.为实现我们整个项目功能而开始实现各个子模块功能.首先实现第一篇列出的分步功能模块的第五步: 5.python连接SQL S ...

  7. 第五次spring会议

    昨天所做之事: 昨天我对软件加上了换肤和透明度等功能. 今天所做之事: 我对软件加上了保密功能. private void 一键转密文ToolStripMenuItem_Click(object se ...

  8. Python11/26--mysql之视图/触发器/事务/存储过程

    视图: 1.什么是视图 视图就是通过查询得到一张虚拟表,然后保存下来,下次用的时候直接使用即可 2.为什么用视图 如果要频繁使用一张虚拟表,可以不用重复查询 3.如何用视图 select * from ...

  9. 综合数据api接口使用

    由于请求数据接口是跨域的,但是我们无法改变接口的代码 先从请求后台,然后从后台进行二次请求,请求数据接口 原生代码 package edu.nf.http.test; import org.apach ...

  10. 第一节 —— vue2.0 环境安装,工程化开发

    vue的开发有两种,一种是直接的在script标签里引入vue.js文件即可,这样子引入的话个人感觉做小型的多页面会比较舒坦,一旦做大型一点的项目,还是离不开webpack. 所以另一种方法也就是基于 ...