使用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" 输出:整型 ...
随机推荐
- 利用百度云盘API上传文件至百度云盘
一.获取Access Token示例 1. 请您将以下HTTP请求直接粘贴到浏览器地址栏内,并按下回车键. https://openapi.baidu.com/oauth/2.0/authorize? ...
- 【BZOJ】2078: [POI2004]WYS
题意: 给n个互不相交的多边形(边均平行于坐标轴),问最大深度.深度的定义是,若多边形A被多边形B包含,则\(dep[A]=max(dep[B])+1\).坐标系的深度为0.(n<=40000, ...
- ThinkPHP3.2.3--相对路径的写法
window.location.href='/index.php/Home/Manager/login' 以 / 开始,而不是 ./
- HDU 1251 Trie树模板题
1.HDU 1251 统计难题 Trie树模板题,或者map 2.总结:用C++过了,G++就爆内存.. 题意:查找给定前缀的单词数量. #include<iostream> #incl ...
- uploadify 自动访问url 初始化 自动请求
摘要: uploadify 自动请求url, 初始化时自动请求url解决方法. 项目中使用了uploadify 上传图片,当访问到上传页面url,uploadify初始化时再一次访问该url 当我在配 ...
- TCP和UDP的聊天
TCP聊天 TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议. 1.一个TCP连接必须要经过三次"对话"才能建立起来,其中的过程非 ...
- 1019 JDBC链接数据库进行修删改查
package com.liu.test01; import java.sql.Statement; import java.sql.Connection; import java.sql.Drive ...
- [转] - hadoop中使用lzo的压缩
在hadoop中使用lzo的压缩算法可以减小数据的大小和数据的磁盘读写时间,不仅如此,lzo是基于block分块的,这样他就允许数据被分解成chunk,并行的被hadoop处理.这样的特点,就可以让l ...
- c#语句 类
知识点: 1.string类 2.Math类 3.DateTime 获取时间 for穷举 1.羽毛球拍15元,球3元,水2元.现有200元,每种至少买一个,共有多少种可能.
- 2016HUAS暑假集训训练2 E - I Hate It
Description 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少.这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老 ...