1. 简单介绍

reduce side  join是全部join中用时最长的一种join,可是这样的方法可以适用内连接、left外连接、right外连接、full外连接和反连接等全部的join方式。reduce side
 join不仅能够对小数据进行join,也能够对大数据进行join,可是大数据会占用大量的集群内部网络IO,由于全部数据终于要写入到reduce端进行join。

假设要做join的数据量很大的话。就不得不用reduce join了。


2. 适用场景
-join的两部分数据量很大;
-想要通过一种模式灵活的适用多种join。

3.Reduce side  join的架构


3.1 map 阶段
map 阶段首先从数据中提取出join的foreign key作为map输出的key,然后将输入的记录所有作为输出value。输出的value须要依据输入的数据集打上数据集的标签,比方在value的开头加上‘A’‘B’的标签。

3.2 reduce阶段
reduce端对具有相同foreign key的数据进行处理,对具有标签'A'和'B'的数据进行迭代处理,下面分别用伪代码对不同的join的处理进行说明。
-内连接:假设带有标签‘A’和‘B’的数据都存在,遍历并连接这些数据,然后输出
if (!listA.isEmpty() && !listB.isEmpty()) {
for (Text A : listA) {
for (Text B : listB) {
context.write(A, B);
}
}
}
-左外连接:右边的数据假设存在就与左边连接,否则将右边的字段都赋null。仅仅输出左边
// For each entry in A,
for (Text A : listA) {
// If list B is not empty, join A and B
if (!listB.isEmpty()) {
for (Text B : listB) {
context.write(A, B);
}
} else {
// Else, output A by itself
context.write(A, EMPTY_TEXT);
}
}

-右外连接:与左外连接类似。左边为空就将左边赋值null,仅仅输出右边
// For each entry in B,
for (Text B : listB) {
// If list A is not empty, join A and B
if (!listA.isEmpty()) {
for (Text A : listA) {
context.write(A, B);
}
} else {
// Else, output B by itself
context.write(EMPTY_TEXT, B);
}
}

-全外连接:这个要相对复杂点,首先输出A和B都不为空的。然后输出某一边为空的
// If list A is not empty
if (!listA.isEmpty()) {
// For each entry in A
for (Text A : listA) {
// If list B is not empty, join A with B
if (!listB.isEmpty()) {
for (Text B : listB) {
context.write(A, B);
}
} else {
// Else, output A by itself
context.write(A, EMPTY_TEXT);
}
}
} else {
// If list A is empty, just output B
for (Text B : listB) {
context.write(EMPTY_TEXT, B);
}
}

-反连接:输出A和B没有共同foreign key的值
// If list A is empty and B is empty or vice versa
if (listA.isEmpty() ^ listB.isEmpty()) {
// Iterate both A and B with null values
// The previous XOR check will make sure exactly one of
// these lists is empty and therefore the list will be skipped
for (Text A : listA) {
context.write(A, EMPTY_TEXT);
}
for (Text B : listB) {
context.write(EMPTY_TEXT, B);
}
}

4.实例
以下举一个简单的样例,要求可以用reduce side join方式实现以上全部的join。


4.1数据
User 表
---------------------------
username cityid
--------------------------
Li lei, 1
Xiao hong, 2
Lily, 3
Lucy, 3
Daive, 4
Jake, 5
Xiao Ming, 6

City表
---------------------------
cityid cityname
--------------------------
1, Shanghai
2, Beijing
3, Jinan
4, Guangzhou
7, Wuhan
8, Shenzhen

4.2 代码介绍
写两个mapper,一个mapper处理user数据,一个mapper处理city数据。在主函数中调用时用MultipleInputs类加入数据路径,并分别指派两个处理的Mapper。
往configuration中加入參数“join.type”,传给reducer,决定在reduce端採用什么样的join。
具体代码例如以下:
package com.study.hadoop.mapreduce;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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.util.GenericOptionsParser;
import org.apache.hadoop.mapreduce.lib.input.MultipleInputs;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class ReduceJoin { //user map
public static class UserJoinMapper extends Mapper<Object, Text, Text, Text>{
private Text outKey = new Text();
private Text outValue = new Text();
@Override
protected void map(Object key, Text value, Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
String line = value.toString();
String[] items = line.split(","); outKey.set(items[1]);
outValue.set("A"+items[0]);
context.write(outKey, outValue);
}
}
//city map
public static class CityJoinMapper extends Mapper<Object, Text, Text, Text>{
// TODO Auto-generated constructor stub
private Text outKey = new Text();
private Text outValue = new Text();
@Override
protected void map(Object key, Text value, Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
String line = value.toString();
String[] items = line.split(","); outKey.set(items[0]);
outValue.set("B"+items[1]);
context.write(outKey, outValue);
} }
public static class JoinReducer extends Reducer<Text, Text, Text, Text>{
// TODO Auto-generated constructor stub
//Join type:{inner,leftOuter,rightOuter,fullOuter,anti}
private String joinType = null;
private static final Text EMPTY_VALUE = new Text("");
private List<Text> listA = new ArrayList<Text>();
private List<Text> listB = new ArrayList<Text>();
@Override
protected void setup(Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
//获取join的类型
joinType = context.getConfiguration().get("join.type");
} @Override
protected void reduce(Text key, Iterable<Text> values,Context context)
throws IOException, InterruptedException {
// TODO Auto-generated method stub
listA.clear();
listB.clear(); Iterator<Text> iterator = values.iterator();
while(iterator.hasNext()){
String value = iterator.next().toString();
if(value.charAt(0)=='A')
listA.add(new Text(value.substring(1)));
if(value.charAt(0)=='B')
listB.add(new Text(value.substring(1)));
}
joinAndWrite(context);
} private void joinAndWrite(Context context)
throws IOException, InterruptedException{
//inner join
if(joinType.equalsIgnoreCase("inner")){
if(!listA.isEmpty() && !listB.isEmpty()) {
for (Text A : listA)
for(Text B : listB){
context.write(A, B);
}
}
}
//left outer join
if(joinType.equalsIgnoreCase("leftouter")){
if(!listA.isEmpty()){
for (Text A : listA){
if(!listB.isEmpty()){
for(Text B: listB){
context.write(A, B);
}
}
else{
context.write(A, EMPTY_VALUE);
}
}
}
}
//right outer join
else if(joinType.equalsIgnoreCase("rightouter")){
if(!listB.isEmpty()){
for(Text B: listB){
if(!listA.isEmpty()){
for(Text A: listA)
context.write(A, B);
}else {
context.write(EMPTY_VALUE, B);
}
}
}
}
//full outer join
else if(joinType.equalsIgnoreCase("fullouter")){
if(!listA.isEmpty()){
for (Text A : listA){
if(!listB.isEmpty()){
for(Text B : listB){
context.write(A, B);
}
}else {
context.write(A, EMPTY_VALUE);
}
}
}else{
for(Text B : listB)
context.write(EMPTY_VALUE, B);
}
}
//anti join
else if(joinType.equalsIgnoreCase("anti")){
if(listA.isEmpty() ^ listB.isEmpty()){
for(Text A : listA)
context.write(A, EMPTY_VALUE);
for(Text B : listB)
context.write(EMPTY_VALUE, B);
}
}
}
} public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
// TODO Auto-generated method stub
Configuration conf = new Configuration();
String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs(); if (otherArgs.length < 4)
{
System.err.println("params:<UserInDir> <CityInDir> <OutDir> <join Type>");
System.exit(1);
}
Job job = new Job(conf,"Reduce side join Job");
job.setJarByClass(ReduceJoin.class);
job.setReducerClass(JoinReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
MultipleInputs.addInputPath(job, new Path(otherArgs[0]), TextInputFormat.class, UserJoinMapper.class);
MultipleInputs.addInputPath(job, new Path(otherArgs[1]), TextInputFormat.class, CityJoinMapper.class);
FileOutputFormat.setOutputPath(job, new Path(otherArgs[2]));
job.getConfiguration().set("join.type", otherArgs[3]); System.exit(job.waitForCompletion(true) ? 0 : 1);
} }

4.3 结果

运行语句:

inner join:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hhb2xvdmVqaWE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">


left outer join:


right outer join:


full outer join:


anti join:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hhb2xvdmVqaWE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">





MapReduce的Reduce side Join的更多相关文章

  1. 0 MapReduce实现Reduce Side Join操作

    一.准备两张表以及对应的数据 (1)m_ys_lab_jointest_a(以下简称表A) 建表语句: create table if not exists m_ys_lab_jointest_a ( ...

  2. hadoop 多表join:Map side join及Reduce side join范例

    最近在准备抽取数据的工作.有一个id集合200多M,要从另一个500GB的数据集合中抽取出所有id集合中包含的数据集.id数据集合中每一个行就是一个id的字符串(Reduce side join要在每 ...

  3. hadoop的压缩解压缩,reduce端join,map端join

    hadoop的压缩解压缩 hadoop对于常见的几种压缩算法对于我们的mapreduce都是内置支持,不需要我们关心.经过map之后,数据会产生输出经过shuffle,这个时候的shuffle过程特别 ...

  4. MapReduce编程之Semi Join多种应用场景与使用

    Map Join 实现方式一 ● 使用场景:一个大表(整张表内存放不下,但表中的key内存放得下),一个超大表 ● 实现方式:分布式缓存 ● 用法: SemiJoin就是所谓的半连接,其实仔细一看就是 ...

  5. Map Reduce Application(Join)

    We are going to explain how join works in MR , we will focus on reduce side join and map side join. ...

  6. mapreduce作业reduce被大量kill掉

    之前有一段时间.我们的hadoop2.4集群压力非常大.导致提交的job出现大量的reduce被kill掉.同样的job执行时间比在hadoop0.20.203上面长了非常多.这个问题事实上是redu ...

  7. Reduce Side Join实现

    关于reduce边join,其最重要的是使用MultipleInputs.addInputPath这个api对不同的表使用不同的Map,然后在每个Map里做一下该表的标识,最后到了Reduce端再根据 ...

  8. Yarn源码分析之参数mapreduce.job.reduce.slowstart.completedmaps介绍

    mapreduce.job.reduce.slowstart.completedmaps是MapReduce编程模型中的一个参数,这个参数的含义是,当Map Task完成的比例达到该值后才会为Redu ...

  9. mapreduce中reduce没有执行

    hadoop执行mapreduce过程reduce不执行原因 1.如果你的map过程中没有context.write()是不执行reduce过程的:2.如果你的map过程中context.write( ...

随机推荐

  1. 迅为IMX6UL开发板

    迅为iMX6UL开发板采用核心板加底板形式,核心板使用邮票孔方式连接,牢固耐用.处理器ARM®Cortex®-A7内核,运行速度高达528 MHz.512MDDR内存,8G EMMC存储,板截双网口, ...

  2. python学习(day2)

    1.常用数据类型及内置方法 1.列表(list) 定义:在中括号[]内存放任意多个值,用逗号隔开. 具体函数和内置方法如下: #定义学生列表,可存放多个学生 students=['a','b','c' ...

  3. splice用法解析

    splice()方法算是最强大的数组方法了,它有很多种用法,主要用于删除指定位置的数组项,在指定的位置插入数组项,在指定位置替换数组项,slpice()方法始终都会返回一个数组,该数组包括从原始数组中 ...

  4. 【东软实训】SQLselect及其相关操作

    SQL select 及相关操作 SQL是用于访问和处理数据库的标准的计算机语言,我们所使用的的是Oracle SQL 一个数据库通常包含一个或多个表,每个表有一个名字表示,下图即为一个名为“emp” ...

  5. POJ 1664 放苹果( 递推关系 )

    链接:传送门 思路:苹果m个,盘子n个.假设 f ( m , n ) 代表 m 个苹果,n个盘子有 f ( m , n ) 种放法. 根据 n 和 m 的关系可以进一步分析: 特殊的 n = 1 || ...

  6. MySQL异常:Caused by: com.mysql.jdbc.exceptions.MySQLTimeoutException: Statement cancelled due to timeout or client request

    Caused by: com.mysql.jdbc.exceptions.MySQLTimeoutException: Statement cancelled due to timeout or cl ...

  7. 初次使用IDEA创建maven项目

    第一次使用IDEA,创建一个maven项目,首先下载maven,官方地址:http://maven.apache.org/download.cgi 解压,在环境变量里配置 path里 D:\maven ...

  8. stark组件之时间插件(九)

    在模型model中用的都是时间字段DateTimeField字段,在后台处理中可以看到,在生成modelform过程中,继承的是BaseModelForm,而其对时间字段加入了特殊的date_time ...

  9. MongoDB数据库的安装

    首先就是MongoDB的下载,可以去MongoDB官网进行下载,https://www.mongodb.com/download-center/community,也可以通过百度网盘直接下载, 链接: ...

  10. Android使用JDBC连接数据库

    连接数据库是安卓开发中几乎不可避免的一项工作,稍有规模的应用通常都需要使用数据库来存储用户数据.对于本地数据库当然可以使用sqlite,而对于多用户线上应用,则一般需要配备云端数据库.其中比较常用且开 ...