使用MapReduce实现一些经典的案例
在工作中,很多时候都是用hive或pig来自动化执行mr统计,但是我们不能忘记原始的mr。本文记录了一些通过mr来完成的经典的案例,有倒排索引、数据去重等,需要掌握。
倒排索引(Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构。通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。
之所以称之为倒排索引,是因为文章内的单词反向检索获取文章标识,从而完成巨大文件的快速搜索。搜索引擎就是利用倒排索引来进行搜索的,此外,倒排索引也是Lucene的实现原理。
假设有两个文件,a.txt类容为“hello you hello”,b.txt内容为“hello hans”,则倒排索引后,期望返回如下内容:
"hello" "a.txt:2;b.txt:1"
"you" "a.txt:1"
"hans" "b.txt:1"
从后想前倒退,要输出结果“"hello" "a.txt:2;b.txt:1"”,则reduce输出为<hello,a.txt:2;b.txt:1>,输入为<hello,a.txt:2>、<hello,b.txt:1>。reduce的输入为map的输出,分一下,要map端直接输出<hello,a.txt:2>这种类型的数据是实现不了的。这时,我们可以借助combine作为中间过渡步骤来实现。combine输入数据为<hello:a.txt,1>、<hello:a.txt,1>、<hello:b.txt,1>,可以转化为符合reduce输入要求的数据,此时map端输出<hello:a.txt,1>类型的数据也是很简单的,实现过程如图1所示。
图1 mapreduce倒排索引实现原理示意图
实现代码如下:
package com.hicoor.hadoop.mapreduce.reverse; import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.StringTokenizer; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.compress.SplitCompressionInputStream;
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.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; //工具类
class StringUtil {
public static String getShortPath(String filePath) {
if (filePath.length() == 0)
return filePath;
return filePath.substring(filePath.lastIndexOf("/") + 1);
} public static String getSplitByIndex(String str, String regex, int index) {
String[] splits = str.split(regex);
if (splits.length < index)
return "";
return splits[index];
}
} public class InverseIndex { public static class ReverseWordMapper extends
Mapper<LongWritable, Text, Text, Text> {
@Override
protected void map(LongWritable key, Text value,
Mapper<LongWritable, Text, Text, Text>.Context context)
throws IOException, InterruptedException {
FileSplit split = (FileSplit) context.getInputSplit();
String fileName = StringUtil.getShortPath(split.getPath()
.toString());
StringTokenizer st = new StringTokenizer(value.toString());
while (st.hasMoreTokens()) {
String word = st.nextToken().toLowerCase();
word = word + ":" + fileName;
context.write(new Text(word), new Text("1"));
}
}
} public static class ReverseWordCombiner extends
Reducer<Text, Text, Text, Text> {
@Override
protected void reduce(Text key, Iterable<Text> values,
Reducer<Text, Text, Text, Text>.Context context)
throws IOException, InterruptedException { long sum = 0;
for (Text value : values) {
sum += Integer.valueOf(value.toString());
}
String newKey = StringUtil.getSplitByIndex(key.toString(), ":", 0);
String fileKey = StringUtil
.getSplitByIndex(key.toString(), ":", 1);
context.write(new Text(newKey),
new Text(fileKey + ":" + String.valueOf(sum)));
}
} public static class ReverseWordReducer extends Reducer<Text, Text, Text, Text> {
@Override
protected void reduce(Text key, Iterable<Text> values,
Reducer<Text, Text, Text, Text>.Context context)
throws IOException, InterruptedException { StringBuilder sb = new StringBuilder("");
for (Text v : values) {
sb.append(v.toString()+" ");
}
context.write(key, new Text(sb.toString()));
}
} private static final String FILE_IN_PATH = "hdfs://hadoop0:9000/reverse/in/";
private static final String FILE_OUT_PATH = "hdfs://hadoop0:9000/reverse/out/"; public static void main(String[] args) throws IOException,
URISyntaxException, ClassNotFoundException, InterruptedException {
System.setProperty("hadoop.home.dir", "D:\\desktop\\hadoop-2.6.0");
Configuration conf = new Configuration(); // 删除已存在的输出目录
FileSystem fileSystem = FileSystem.get(new URI(FILE_OUT_PATH), conf);
if (fileSystem.exists(new Path(FILE_OUT_PATH))) {
fileSystem.delete(new Path(FILE_OUT_PATH), true);
} Job job = Job.getInstance(conf, "InverseIndex");
job.setJarByClass(InverseIndex.class);
job.setMapperClass(ReverseWordMapper.class);
job.setCombinerClass(ReverseWordCombiner.class);
job.setReducerClass(ReverseWordReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
FileInputFormat.addInputPath(job, new Path(FILE_IN_PATH));
FileOutputFormat.setOutputPath(job, new Path(FILE_OUT_PATH));
job.waitForCompletion(true);
}
}
TopK问题指在海量数据中查找某条件排名前K名的记录,如在用户存款记录中查找存款余额最大的前3名用户。当数据量不大时,可以直接加载到单机内存中进行处理,但是当数据量非常庞大时,需要借助mapreduce来分布式处理。可以使用HiveQL来处理,也可以自己编写mapduce程序来处理此问题。
实现原理:在每个map任务中查询并返回当前处理数据最大的top k条记录,然后将所有map输出的记录交由一个reduce任务处理,查找并返回最终的top k记录,过程如图2所示。
图2 mapreduce实现top k过程示意图
需要注意的是,这里reduce个数只能为1个,并且不需要设置Combiner。
假设存在文件deposit1.txt和deposit2.txt,其内容分别为(列分别表示用户名与存款金额):
deposit1.txt
p1 125
p2 23
p3 365
p4 15
p5 188 deposit2.txt
p6 236
p7 115
p8 18
p9 785
p10 214
要求找出存款金额最大的前3位用户,参考实现代码:
package com.hicoor.hadoop.mapreduce; import java.io.IOException;
import java.net.URI;
import java.util.Comparator;
import java.util.TreeMap; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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 MapReduceTopKDemo { public static final int K = 3; //默认的TreeMap是按key升序排列 此方法用于获取降序排列的TreeMap
private static TreeMap<Long, String> getDescSortTreeMap() {
return new TreeMap<Long, String>(new Comparator<Long>() {
@Override
public int compare(Long o1, Long o2) {
return o2.compareTo(o1);
}
});
} static class TopKMapper extends Mapper<LongWritable, Text, LongWritable, Text> {
private TreeMap<Long, String> map = getDescSortTreeMap(); @Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, LongWritable, Text>.Context context)
throws IOException, InterruptedException { String line = value.toString();
if(line == null || line == "") return;
String[] splits = line.split("\t");
if(splits.length < 2) return; map.put(Long.parseLong(splits[1]), splits[0]);
//只保留最大的K个数据
if(map.size() > K) {
//由于记录按照key降序排列 只需删除最后一个记录
map.remove(map.lastKey());
}
} @Override
protected void cleanup(Mapper<LongWritable, Text, LongWritable, Text>.Context context)
throws IOException, InterruptedException { for (Long num : map.keySet()) {
context.write(new LongWritable(num), new Text(map.get(num)));
}
}
} static class TopKReducer extends Reducer<LongWritable, Text, Text, LongWritable> {
private TreeMap<Long, String> map = getDescSortTreeMap(); @Override
protected void reduce(LongWritable key, Iterable<Text> value, Reducer<LongWritable, Text, Text, LongWritable>.Context context)
throws IOException, InterruptedException { StringBuilder ps = new StringBuilder();
for (Text val : value) {
ps.append(val.toString());
} map.put(key.get(), ps.toString());
if(map.size() > K) {
map.remove(map.lastKey());
}
} @Override
protected void cleanup(Reducer<LongWritable, Text, Text, LongWritable>.Context context)
throws IOException, InterruptedException { for (Long num : map.keySet()) {
context.write(new Text(map.get(num)), new LongWritable(num));
}
}
} private final static String FILE_IN_PATH = "hdfs://cluster1/topk/in";
private final static String FILE_OUT_PATH = "hdfs://cluster1/topk/out"; /* TopK问题:在海量数据中查找某条件排名前K名的记录,如在用户存款记录中查找存款余额最大的前3名用户
* 1) 测试输入数据(列分别表示用户账户与存款余额):
* p1 125
* p2 23
* p3 365
* p4 15
* p5 188
* p6 236
* p7 115
* p8 18
* p9 785
* p10 214
* 2) 输出结果:
* p9 785
* p3 365
* p6 236
*/
public static void main(String[] args) throws Exception {
System.setProperty("hadoop.home.dir", "D:\\desktop\\hadoop-2.6.0");
Configuration conf = getHAContiguration(); // 删除已存在的输出目录
FileSystem fileSystem = FileSystem.get(new URI(FILE_OUT_PATH), conf);
if (fileSystem.exists(new Path(FILE_OUT_PATH))) {
fileSystem.delete(new Path(FILE_OUT_PATH), true);
} Job job = Job.getInstance(conf, "MapReduce TopK Demo");
job.setMapperClass(TopKMapper.class);
job.setJarByClass(MapReduceTopKDemo.class);
job.setReducerClass(TopKReducer.class);
job.setMapOutputKeyClass(LongWritable.class);
job.setMapOutputValueClass(Text.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
FileInputFormat.addInputPath(job, new Path(FILE_IN_PATH));
FileOutputFormat.setOutputPath(job, new Path(FILE_OUT_PATH));
job.waitForCompletion(true);
} private static Configuration getHAContiguration() {
Configuration conf = new Configuration();
conf.setStrings("dfs.nameservices", "cluster1");
conf.setStrings("dfs.ha.namenodes.cluster1", "hadoop1,hadoop2");
conf.setStrings("dfs.namenode.rpc-address.cluster1.hadoop1", "172.19.7.31:9000");
conf.setStrings("dfs.namenode.rpc-address.cluster1.hadoop2", "172.19.7.32:9000");
conf.setStrings("dfs.client.failover.proxy.provider.cluster1", "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
return conf;
} }
执行结果为:
p9 785
p3 365
p6 236
使用MapReduce实现一些经典的案例的更多相关文章
- PE经典DIY案例1:全解开方案让量产PE也能
更新说明:因未来的uefi似乎并不能识别并引导ud区,但能识别和引导量产和u+B+隐藏或高端隐藏区,故解决量产PE对u+B+隐藏区的支持,并增加对UEFI启动支持,已经成为PE制作的最主流技术. PE ...
- 18个awk的经典实战案例
介绍 这些案例是我收集起来的,大多都是我自己遇到过的,有些比较经典,有些比较具有代表性. 这些awk案例我也录了相关视频的讲解awk 18个经典实战案例精讲,欢迎大家去瞅瞅. 插入几个新字段 在&qu ...
- Spring框架-经典的案例和demo,一些可以直接用于生产,使用atomikos来处理多数据源的一致性事务等
Spring Examples Demo website:http://www.ityouknow.com/ 对Spring框架的学习,包括一些经典的案例和demo,一些可以直接用于生产. sprin ...
- 【Hadoop离线基础总结】MapReduce自定义InputFormat和OutputFormat案例
MapReduce自定义InputFormat和OutputFormat案例 自定义InputFormat 合并小文件 需求 无论hdfs还是mapreduce,存放小文件会占用元数据信息,白白浪费内 ...
- 快要C语言考试了,大学生们收好这些经典程序案例,包你考试过关!
距离考试越来越近 编程大佬早已饥渴难耐 电脑小白还在瑟瑟发抖 但是不要怕! 来看看这些经典程序案例 包你考试过关! [程序1] 有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多 ...
- JAVA并发,经典死锁案例-哲学家就餐
转自:http://blog.csdn.net/tayanxunhua/article/details/38691005 死锁经典案例:哲学家就餐. 这个案例会导致死锁. 通过修改<Java编程 ...
- MySQL数据库“十宗罪”【十大经典错误案例】
原文作者:张甦 来源:http://blog.51cto.com/sumongodb 今天就给大家列举 MySQL 数据库中,最经典的十大错误案例,并附有处理问题的解决思路和方法,希望能给刚入行,或数 ...
- Mapreduce之排序&规约&实战案例
MapReduce 排序和序列化 简单介绍 ①序列化 (Serialization) 是指把结构化对象转化为字节流②反序列化 (Deserialization) 是序列化的逆过程. 把字节流转为结构化 ...
- C语言经典88案例,我文科妹妹说她都学会了!
案例ex01: 将字符串转换为一个整数 1 题目 函数:fun() 功能:将字符串转换为一个整数 描述: [不能使用C语言提供的字符串函数] 输入:字符串"-1234" 输出:整型 ...
随机推荐
- PHP面向对象学习五 类中接口的应用
类中接口的应用 接口:一种成员属性全部为抽象的特殊抽象类,在程序中同为规范的作用 抽象类:1.类中至少有一个抽象方法.2.方法前需要加abstract 接口: 1.类中全部为抽象方法,抽象方法前不 ...
- Java_cookie 和session 的区别详解
这些都是基础知识,不过有必要做深入了解.先简单介绍一下. 二者的定义: 当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cookie 会帮你在网站上所打的文字或是一些选择, 都纪 ...
- nginx 中 PHP 调用PEAR.php遇到的问题
公司有个老项目,写了很多年了,是在apache 上面跑的,无意间,我想让它跑到nginx上,结果遇到了PEAR.php的问题,先安装pear 基本安富有就是 wget http://pear.php. ...
- Oracle connect by 树查询之三(超详细)
查找员工编号为7369的领导: 1 SELECT LEVEL,E.* FROM EMP E CONNECT BY PRIOR E.MGR = E.EMPNO START WITH E.EMPNO = ...
- osg学习示例之遇到问题四骨骼动画编译osgCal
osg学习示例之遇到问题四骨骼动画编译osgCal 转自:http://blog.csdn.net/wuwangrun/article/details/8239451 今天学到书<OpenSce ...
- Node.js前端自动化工具:gulp
前端自动化工具 -- gulp 使用简介 gulp是基于流的前端自动化构建工具. 之前也谈到了 grunt的用法,grunt其实就是配置+配置的形式. 而gulp呢,是基于stream流的形式,也就是 ...
- java设计模式。。。转载
maowang I am a slow walker,but I never walk backwards! 博客园 首页 新随笔 联系 订阅 管理 随笔 - 125 文章 - 0 评论 - 12 ...
- Displaying a full list of groups in Odoo's Kanban view
Kanban view is probably the most flexible view in Odoo. It can be used for many different purposes. ...
- Linux下配置Lamp
linux下配置lamp步骤: 一.快速安装Apache+PHP5+MySql 先更新: # yum update 然后安装LAMP环境:(163的yum源上只有php5.1.6 mysql 5.0. ...
- HDU1166线段树(单点更新,区间求和)
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...