一.准备两张表以及对应的数据

(1)m_ys_lab_jointest_a(以下简称表A)

建表语句:

create table if not exists m_ys_lab_jointest_a (
id bigint,
name string
)
row format delimited
fields terminated by ''
lines terminated by ''
stored as textfile;  

具体数据如下:

id    name
1 北京
2 天津
3 河北
4 山西
5 内蒙古
6 辽宁
7 吉林
8 黑龙江
 
 
 
 
 
 
 
 
 
 
(2)m_ys_lab_jointest_b(以下简称表B)
建表语句为:
create table if not exists m_ys_lab_jointest_b (
id bigint,
statyear bigint,
num bigint
)
row format delimited
fields terminated by ''
lines terminated by ''
stored as textfile;
id   statyear  num
1 2010 1962
1 2011 2019
2 2010 1299
2 2011 1355
4 2010 3574
4 2011 3593
9 2010 2303
9 2011 2347

我们的目的是,以id为key做join操作,得到以下表:m_ys_lab_jointest_ab

id     name    statyear     num
1       北京    2011    2019
1       北京    2010    1962
2       天津    2011    1355
2       天津    2010    1299
4       山西    2011    3593
4       山西    2010    3574

二.计算模型

整个计算过程是:

(1)在map阶段,把所有记录标记成<key, value>的形式,其中key是id,value则根据来源不同取不同的形式:来源于表A的记录,value的值为"a#"+name;来源于表B的记录,value的值为"b#"+score。
(2)在reduce阶段,先把每个key下的value列表拆分为分别来自表A和表B的两部分,分别放入两个向量中。然后遍历两个向量做笛卡尔积,形成一条条最终结果。
 如下图所示:

上代码:

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileSplit;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter; /**
* MapReduce实现Join操作
*/
public class MapRedJoin {
public static final String DELIMITER = "\u0009"; // 字段分隔符 // map过程
public static class MapClass extends MapReduceBase implements Mapper<LongWritable, Text, Text, Text> {
public void configure(JobConf job) {
super.configure(job);
} public void map(LongWritable key, Text value, OutputCollector<Text, Text> output, Reporter reporter) throws IOException, ClassCastException {
// 获取输入文件的全路径和名称
String filePath = ((FileSplit)reporter.getInputSplit()).getPath().toString();
// 获取记录字符串
String line = value.toString();
// 抛弃空记录
if (line == null || line.equals("")){
return;
}
// 处理来自表A的记录
if (filePath.contains("m_ys_lab_jointest_a")) {
String[] values = line.split(DELIMITER); // 按分隔符分割出字段
if (values.length < 2){
return;
}
String id = values[0]; // id
String name = values[1]; // name
output.collect(new Text(id), new Text("a#"+name));
} else if (filePath.contains("m_ys_lab_jointest_b")) {// 处理来自表B的记录
String[] values = line.split(DELIMITER); // 按分隔符分割出字段
if (values.length < 3){
return;
}
String id = values[0]; // id
String statyear = values[1]; // statyear
String num = values[2]; //num
output.collect(new Text(id), new Text("b#"+statyear+DELIMITER+num));
}
}
} // reduce过程
public static class Reduce extends MapReduceBase implements Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterator<Text> values, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {
List<String> listA = new ArrayList<String>(); // 存放来自表A的值
List<String> listB = new ArrayList<String>(); // 存放来自表B的值
while (values.hasNext()) {
String value = values.next().toString();
if (value.startsWith("a#")) {
listA.add(value.substring(2));
} else if (value.startsWith("b#")) {
listB.add(value.substring(2));
}
}
int sizeA = listA.size();
int sizeB = listB.size();
// 遍历两个向量
int i, j;
for (i = 0; i < sizeA; i ++) {
for (j = 0; j < sizeB; j ++) {
output.collect(key, new Text(listA.get(i) + DELIMITER +listB.get(j)));
}
}
}
} protected void configJob(JobConf conf) {
conf.setMapOutputKeyClass(Text.class);
conf.setMapOutputValueClass(Text.class);
conf.setOutputKeyClass(Text.class);
conf.setOutputValueClass(Text.class);
conf.setOutputFormat(ReportOutFormat.class);
}
}

三.技术细节

下面说一下其中的若干技术细节:
(1)由于输入数据涉及两张表,我们需要判断当前处理的记录是来自表A还是来自表B。Reporter类getInputSplit()方法可以获取输入数据的路径,具体代码如下:
String filePath = ((FileSplit)reporter.getInputSplit()).getPath().toString();
(2)map的输出的结果,同id的所有记录(不管来自表A还是表B)都在同一个key下保存在同一个列表中,在reduce阶段需要将其拆开,保存为相当于笛卡尔积的m x n条记录。由于事先不知道m、n是多少,这里使用了两个向量(可增长数组)来分别保存来自表A和表B的记录,再用一个两层嵌套循环组织出我们需要的最终结果。
(3)在MapReduce中可以使用System.out.println()方法输出,以方便调试。不过System.out.println()的内容不会在终端显示,而是输出到了stdout和stderr这两个文件中,这两个文件位于logs/userlogs/attempt_xxx目录下。可以通过web端的历史job查看中的“Analyse This Job”来查看stdout和stderr的内容。

0 MapReduce实现Reduce Side Join操作的更多相关文章

  1. MapReduce的Reduce side Join

    1. 简单介绍 reduce side  join是全部join中用时最长的一种join,可是这样的方法可以适用内连接.left外连接.right外连接.full外连接和反连接等全部的join方式.r ...

  2. 使用MapReduce实现join操作

     在关系型数据库中,要实现join操作是非常方便的,通过sql定义的join原语就可以实现.在hdfs存储的海量数据中,要实现join操作,可以通过HiveQL很方便地实现.不过HiveQL也是转化成 ...

  3. MapReduce 实现数据join操作

    前段时间有一个业务需求,要在外网商品(TOPB2C)信息中加入 联营自营 识别的字段.但存在的一个问题是,商品信息 和 自营联营标示数据是 两份数据:商品信息较大,是存放在hbase中.他们之前唯一的 ...

  4. Hadoop基础-MapReduce的Join操作

    Hadoop基础-MapReduce的Join操作 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.连接操作Map端Join(适合处理小表+大表的情况) no001 no002 ...

  5. Hadoop学习之路(二十一)MapReduce实现Reduce Join(多个文件联合查询)

    MapReduce Join 对两份数据data1和data2进行关键词连接是一个很通用的问题,如果数据量比较小,可以在内存中完成连接. 如果数据量比较大,在内存进行连接操会发生OOM.mapredu ...

  6. 案例-使用MapReduce实现join操作

    哈喽-各位小伙伴们中秋快乐,好久没更新新的文章啦,今天分享如何使用mapreduce进行join操作. 在离线计算中,我们常常不只是会对单一一个文件进行操作,进行需要进行两个或多个文件关联出更多数据, ...

  7. mapreduce join操作

    上次和朋友讨论到mapreduce,join应该发生在map端,理由太想当然到sql里面的执行过程了 wheremap端 join在map之前(笛卡尔积),但实际上网上看了,mapreduce的笛卡尔 ...

  8. Mapreduce中的join操作

    一.背景 MapReduce提供了表连接操作其中包括Map端join.Reduce端join还有半连接,现在我们要讨论的是Map端join,Map端join是指数据到达map处理函数之前进行合并的,效 ...

  9. [MapReduce_add_4] MapReduce 的 join 操作

    0. 说明 Map 端 join && Reduce 端 join 1. Map 端 join Map 端 join:大表+小表 => 将小表加入到内存,迭代大表每一行,与之进行 ...

随机推荐

  1. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

    问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...

  2. 《Effective Java》-——用私有构造器或者枚举类型强化Singleton属性

    Singleton指仅仅被实例化一次的类.Singleton通常被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统.使类成为Singleton会使它的客户端测试变得十分困难,因为无法给Si ...

  3. ABP之Setting

    介绍 每个应用程序都需要存储一些设置,并在应用程序的某个地方使用这些设置.ABP提供了一个强大的基础设施来存储/检索在服务器端和客户端都可用的应用程序.租户和用户级别设置. 设置是通常存储在数据库(或 ...

  4. Android短视频中如何实现720P磨皮美颜录制?

    视频中磨皮.美颜功能已成为刚需,那么如何在Android短视频中实现720P磨皮美颜录制?本篇文章中,网易云信资深开发工程师将向大家介绍具体的操作方法. 相关阅读推荐 <短视频技术详解:Andr ...

  5. Storm 学习之路(九)—— Storm集成Kafka

    一.整合说明 Storm官方对Kafka的整合分为两个版本,官方说明文档分别如下: Storm Kafka Integration : 主要是针对0.8.x版本的Kafka提供整合支持: Storm ...

  6. rbash限制用户执行的命令

    rbash限制用户执行的命令 软连接 sudo ln -s /bin/bash /bin/rbash sudo bash -c 'echo "/bin/rbash" >> ...

  7. Ruby字符串(1):String基本用法

    String字符串 字符串由String类提供,除了直接使用单双引号或其它字面量创建字符串,也可以使用String.new()方法来创建. a = "hello" b = Stri ...

  8. C#类成员初始化顺序

    这里直接给出C#类成员一般初始化顺序: 子类静态字段 子类静态构造 子类实例字段 父类静态字段 父类静态构造 父类实例字段 父类实例构造 子类实例构造 为什么说是"一般"初始化顺序 ...

  9. 统计学习方法9—EM算法

      EM算法是一种迭代算法,是一种用于计算包含隐变量概率模型的最大似然估计方法,或极大后验概率.EM即expectation maximization,期望最大化算法. 1. 极大似然估计   在概率 ...

  10. HDU 6060:RXD and dividing(DFS)

    题目链接 题意 给出n个点,要把除1以外的点分成k个集合,然后对于每个集合要和1这个点一起求一个最小生成树,然后问这k个最小生成树的最大总和是多少. 思路 因为每个集合都包含1这个点,因此对于每个点都 ...