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

(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. 章节十四、8-javaScript弹框处理

    一.javaScript弹框没有id.也没有xpath,在F12开发者选项中无法直接通过鼠标去选择弹窗来确定元素在代码中的位置. 弹窗有两种,一种实只有"确定"按钮的alert类型 ...

  2. SpringBoot(十九)_spring.profiles.active=@profiles.active@ 的使用

    现在在的公司用spring.profiles.active=@profiles.active@ 当我看到这个的时候,一脸蒙蔽,这个@ 是啥意思. 这里其实是配合 maven profile进行选择不同 ...

  3. 快速开发第一个SpringBoot应用

    通过笔者这段实践SpringBoot的学习,发现自从使用了SpringBoot后,就再也回不去SpringMVC了,因为相比于SpringMVC,SpringBoot真是太高效率了.下面我们看看它效率 ...

  4. 第一章 corejava的入门

    第一章 corejava的入门一:什么是语言语言=os+数据结构+算法+思想os:操作系统数据结构:队,栈,二叉树,链表算法:做游戏开发时非常重要面试题:int a>0,b>0只使用一条输 ...

  5. Ubuntu 16.04.3启动MySQL报错

    今天安装mysql,连接MySQL时报错mysql: [Warning] Using a password on the command line interface can be insecure. ...

  6. 学习 GitHub 有什么好处?

    layout: post title: "学习 GitHub 有什么好处?" date: 2018-04-15 19:20:20 +0800 --- 鸣谢:王顶 老师(河北经贸大学 ...

  7. React躬行记(6)——事件

    React在原生事件的基础上,重新设计了一套跨浏览器的合成事件(SyntheticEvent),在事件传播.注册方式.事件对象等多个方面都做了特别的处理. 一.注册事件 合成事件采用声明式的注册方式, ...

  8. 记2017青岛ICPC

    2017青岛ICPC 11月4日 早上很早到达了青岛,然后去报道,走了好久的校园,穿的很少冷得瑟瑟发抖.中午教练请吃大餐,吃完饭就去热身赛了. 开幕式的时候,教练作为教练代表讲话,感觉周围的队伍看过来 ...

  9. 分组在re模块中的使用

    import re #search s = "<a>wahaha</a>" #标签语言 html 和 web相关 ret= re.search(" ...

  10. Linux命令学习-cp命令

    Linux中,cp命令的全称是copy,主要作用是复制文件或文件夹,类似于Windows下的复制功能. 假设当前处于wintest用户的主目录,路径为 /home/wintest ,存在文件夹test ...