十二道MR习题 - 4 - TopN问题
题目:
有一个很大的文件,这文件中的内容全部都是数字,要求尝试从这个文件中找出最大的10个数字。
分析:
看起来像是一个比较简单的问题。不用大数据框架的话,也能比较轻易的实现:就是逐个读取文件中的每个数字,放到一个大顶堆结构中;将大顶堆放满以后,每读取一个数字就将之和大顶堆中的最小值进行比较,如果其大于这个最小值的话,就将其放入堆中,并将堆中的最小值删除;这样读取到最后,堆中剩下来的内容就是top 10了。
用MapReduce实现的话也说不上困难:我们只使用Map任务读取文件,而reduce中输出的内容就是一个有序的结果集,那么后十位自然就是Top10了。这方案虽说可行,但绝说不上是好的方案。
换个思路:map任务中先完成一轮过滤(没必要多添一重Combiner),先取出每个Map中的top10来,而后在reduce中再进行一轮筛选,从所有map的top10中再选出个top10来。这样处理效率应该会高一些。
看看实现过程:
package com.zhyea.dev; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.IOException;
import java.util.TreeSet; public class TopN { private static final Logger logger = LoggerFactory.getLogger(TopN.class); public static class SplitterMapper extends Mapper<Object, Text, IntWritable, NullWritable> { private static final IntWritable intWritable = new IntWritable(); private static final TreeSet<Integer> set = new TreeSet<>(); @Override
public void map(Object key, Text value, Context context) {
int num = Integer.valueOf(value.toString()); if (set.size() < 10) {
set.add(num);
return;
} if (num > set.first()) {
set.add(num);
set.pollFirst();
}
} @Override
public void cleanup(Context context) {
for (Integer i : set) {
intWritable.set(i);
try {
context.write(intWritable, NullWritable.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
} public static class IntegrateReducer extends Reducer<IntWritable, NullWritable, IntWritable, NullWritable> { private static final IntWritable intWritable = new IntWritable();
private static final TreeSet<Integer> set = new TreeSet<>(); @Override
public void reduce(IntWritable key, Iterable<NullWritable> values, Context context) {
try {
int num = key.get();
if (set.size() < 10) {
set.add(num);
return;
} if (num > set.first()) {
set.add(num);
set.pollFirst();
}
} catch (Exception e) {
e.printStackTrace();
}
} @Override
public void cleanup(Context context) {
for (Integer i : set) {
intWritable.set(i);
try {
context.write(intWritable, NullWritable.get());
} catch (Exception e) {
e.printStackTrace();
}
}
} } public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "top-n");
job.setJarByClass(TopN.class); job.setMapperClass(SplitterMapper.class);
job.setReducerClass(IntegrateReducer.class); job.setOutputKeyClass(IntWritable.class);
job.setOutputValueClass(NullWritable.class); FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1);
} }
程序里在map或reduce方法中没有做任何输出,只是实现了比较逻辑,真正的输出是在cleanup方法中完成的。
用spark实现的话可以先做全排序,然后排重,take前N个记录就可以了。当然也可以按照上面的思路来做实现,下面的代码就是按照我们前面的思路来做的实现:
package com.zhyea.dev
import java.util
import org.apache.hadoop.io.{LongWritable, Text}
import org.apache.hadoop.mapred.TextInputFormat
import org.apache.spark.{SparkConf, SparkContext}
import collection.JavaConversions.asScalaIterator
object TopTen {
def main(args: Array[String]): Unit = {
val inputPath = args(0)
val outputPath = args(1)
val conf = new SparkConf().setAppName("Top Ten")
val sc = new SparkContext(conf)
val data = sc.hadoopFile[LongWritable, Text, TextInputFormat](inputPath)
data.mapPartitions[Long](findTopTen)
.repartition(1)
.distinct()
.sortBy(_.toLong, false)
.mapPartitions(itr => itr.slice(0, 10))
.saveAsTextFile(outputPath)
def findTopTen(itr: Iterator[(LongWritable, Text)]) = {
val set = new util.TreeSet[Long]()
itr.foreach(p => {
val v = p._2.toString.toLong
if (set.size <= 10) {
set.add(v)
} else if (v > set.first) {
set.pollFirst()
set.add(v)
}
})
set.iterator
}
}
}
############################
十二道MR习题 - 4 - TopN问题的更多相关文章
- 十二道MR习题 - 2 - 多文件保存
题目: 需要将MR的执行结果保存到3个文件中,该怎么做. 又是一个送分题. 对于Hadoop的MapReduce来说只需要设置一下reduce任务的数量即可.MR的Job默认reduce数量是1,需要 ...
- 十二道MR习题 - 3 - 交集并集差集
题目 有两个文件A和B,两个文件中都有几百万行数字,现在需要找出A文件和B文件中数字集合的交集.并集.以及A对B的差集. 简单说一下思路: 这个问题关键在于key和value的设计.这里我将文件中的数 ...
- 十二道MR习题 – 1 – 排序
题目: 一个文件,大小约为100G.文件的每一行都是一个数字,要求对文件中的所有数字进行排序. 对于这个题目,了解过Hadoop的同学可以笑而不语了.即使用spark实现也是非常简单的事情. 先说下如 ...
- C primer plus 第五版十二章习题
看完C prime plus(第五版)第十二章,随带完成了后面的习题. 1.不使用全局变量,重写程序清单12.4的程序. 先贴出12.4的程序,方便对照: /* global.c --- 使用外部变量 ...
- hadoop生态系统学习之路(十)MR将结果输出到hbase
之前讲了MR将结果输出到hdfs.hive.db,今天再给大家分享一下,怎样将结果输出到hbase. 首先,提一句,笔者在hadoop集群运行此MR的时候报了一个错误.是一个jar包的缘故,这个错误是 ...
- C和指针 第十六章 习题
16.8 计算平均年龄 #include <stdlib.h> #include <stdio.h> #define MAX_LEN 512 int main() { int ...
- C和指针 第十五章 习题
15.8 十六进制倾印码 #include <stdio.h> #include <stdlib.h> #include <string.h> #include & ...
- C和指针 第十四章 习题
14.1 打印函数 #include <stdio.h> void print_ledger_long(){ printf("function print_ledger_long ...
- 视觉slam十四讲习题ch3-6
题目回顾: 一般解线性方程Ax=b有哪几种做法?你能在Eigen中实现吗? 解: 线性方程组Ax = b的解法 : 1.直接法:(1,2,3,4,5) 2.迭代法:如Jacobi迭代法(6) 其中只有 ...
随机推荐
- delphi------项目类型
Console Application:控制台应用程序 writeln('HelloWorld'); //接收用户输入字符 readln: //直到用户输入回车结束 VCL Forms Applica ...
- Powershell数据处理
1.导出csv文档 Export-Csv D:\ps\xxx.csv -Encoding UTF8 -NoTypeInformation 2.发送mail $from="frommailad ...
- tableView的使用(一)
//tableView的创建 //1.初始化 initWithFrame:style:(plian,gronp) //2.设置属性(行高, 分割线, 表头, 表尾) //3.添加到父 ...
- java中的printf
转载自: http://www.cnblogs.com/healthy-tree/archive/2012/08/07/2626665.html http://www.cnblogs.com/Tank ...
- delphi inifile 支持 utf8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 3 ...
- 转!java操作redis
package sgh.main.powersite; import java.util.ArrayList; import java.util.HashMap; import java.util.I ...
- 删除DOM元素 parent.removeChild(target)
p.removeChild(p.children[0]);
- 序列化组件之生成hypermedialink
一 生成hypermedialink(极少数) 组件 class BooksSerializer(serializers.ModelSerializer): name = serializers.C ...
- mysql分组取每组前几条记录(排名)
1.创建表 create table tb( name varchar(10), val int, memo varchar(20) ); 2.插入数据 insert into tb values(' ...
- <context-param>与<init-param>的差别与作用
<context-param>的作用: web.xml的配置中<context-param>配置作用 1. 启动一个WEB项目的时候,容器(如:Tomcat)会去读它的配置文件 ...