在工作中,很多时候都是用hive或pig来自动化执行mr统计,但是我们不能忘记原始的mr。本文记录了一些通过mr来完成的经典的案例,有倒排索引、数据去重等,需要掌握。

一、使用mapreduce实现倒排索引

   倒排索引(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);
}
}
二、使用mapreduce实现TopK查询

   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实现一些经典的案例的更多相关文章

  1. PE经典DIY案例1:全解开方案让量产PE也能

    更新说明:因未来的uefi似乎并不能识别并引导ud区,但能识别和引导量产和u+B+隐藏或高端隐藏区,故解决量产PE对u+B+隐藏区的支持,并增加对UEFI启动支持,已经成为PE制作的最主流技术. PE ...

  2. 18个awk的经典实战案例

    介绍 这些案例是我收集起来的,大多都是我自己遇到过的,有些比较经典,有些比较具有代表性. 这些awk案例我也录了相关视频的讲解awk 18个经典实战案例精讲,欢迎大家去瞅瞅. 插入几个新字段 在&qu ...

  3. Spring框架-经典的案例和demo,一些可以直接用于生产,使用atomikos来处理多数据源的一致性事务等

    Spring Examples Demo website:http://www.ityouknow.com/ 对Spring框架的学习,包括一些经典的案例和demo,一些可以直接用于生产. sprin ...

  4. 【Hadoop离线基础总结】MapReduce自定义InputFormat和OutputFormat案例

    MapReduce自定义InputFormat和OutputFormat案例 自定义InputFormat 合并小文件 需求 无论hdfs还是mapreduce,存放小文件会占用元数据信息,白白浪费内 ...

  5. 快要C语言考试了,大学生们收好这些经典程序案例,包你考试过关!

    距离考试越来越近 编程大佬早已饥渴难耐 电脑小白还在瑟瑟发抖 但是不要怕! 来看看这些经典程序案例 包你考试过关! [程序1] 有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多 ...

  6. JAVA并发,经典死锁案例-哲学家就餐

    转自:http://blog.csdn.net/tayanxunhua/article/details/38691005 死锁经典案例:哲学家就餐. 这个案例会导致死锁. 通过修改<Java编程 ...

  7. MySQL数据库“十宗罪”【十大经典错误案例】

    原文作者:张甦 来源:http://blog.51cto.com/sumongodb 今天就给大家列举 MySQL 数据库中,最经典的十大错误案例,并附有处理问题的解决思路和方法,希望能给刚入行,或数 ...

  8. Mapreduce之排序&规约&实战案例

    MapReduce 排序和序列化 简单介绍 ①序列化 (Serialization) 是指把结构化对象转化为字节流②反序列化 (Deserialization) 是序列化的逆过程. 把字节流转为结构化 ...

  9. C语言经典88案例,我文科妹妹说她都学会了!

    案例ex01: 将字符串转换为一个整数 1 题目 函数:fun() 功能:将字符串转换为一个整数 描述: [不能使用C语言提供的字符串函数] 输入:字符串"-1234" 输出:整型 ...

随机推荐

  1. [Leetcode] Permutation Sequence

    The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the p ...

  2. Invalid escape sequence(valid ones are \b \t \n \f \r \" \' \\)

    Invalid escape sequence(valid ones are \b \t \n \f \r \" \' \\) 在运行eclipse的相关程序代码时遇到了报错信息,查看控制台 ...

  3. Bin Packing

    Bin Packing 题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=85904#problem/F 题目: A set of  ...

  4. uploadify 自动访问url 初始化 自动请求

    摘要: uploadify 自动请求url, 初始化时自动请求url解决方法. 项目中使用了uploadify 上传图片,当访问到上传页面url,uploadify初始化时再一次访问该url 当我在配 ...

  5. [CareerCup] 18.10 Word Transform 单词转换

    18.10 Given two words of equal length that are in a dictionary, write a method to transform one word ...

  6. [CareerCup] 18.6 Smallest One Million Numbers 最小的一百万个数字

    18.6 Describe an algorithm to find the smallest one million numbers in one billion numbers. Assume t ...

  7. 快速安装zabbix agent并部署监控

    1.准备yum源: epel源:yum install -y zabbix22-agent 2.上传脚本: 上传脚本事先写好的监控脚本到/script/下面 3.修改配置文件:Server=10.10 ...

  8. Ajax请求中的Redirect()

    页面中有一个IsLogin()方法,用以判断该请求的触发者是否登录,如果登录了,则执行查询操作,如果没有登录,则Redirect()至登录界面 页面使用了较多的Ajax请求来获取数据,而在Ajax请求 ...

  9. 测试常用SQL注入语句大全

    转载自Cracer,标题:<渗透常用SQL注入语句大全>,链接http://www.xxxx.com/?p=2226 1.判断有无注入点 整形参数判断 1.直接加' 2.and 1=1 3 ...

  10. css背景图片,bootstrap和jquery-ui结合使用,dialog案例

    css将一个不能铺满整个屏幕的图片铺满整个屏幕,将一下代码放到body中 <img src="image/login6.jpg" width="100%" ...