join连接

MapReduce能够执行大型数据集间的连接(join)操作。连接操作的具体实现技术取决于数据集的规模及分区方式
连接操作如果由mapper执行,则称为“map端连接”;如果由reducer执行,则称为“reduce端连接”。

Map端连接

在两个大规模输入数据集之间的map端连接会在数据到达map函数之前就执行连接操作。为达到该目的,各map的输入数据必须先分区并且以特定方式排序。各个输入数据集被划分成相同数量的分区,并且均按相同的键(连接键)排序。同一键的所有记录均会放在同一分区之中。
Map端连接操作可以连接多个作业的输出,只要这些作业的reducer数量相同、键相同并且输出文件是不可切分的(例如,小于一个HDFS块,或gzip压缩)。

Reduce端连接

由于reduce端连接并不要求输入数据集符合特定结构,因而reduce端连接比map端连接更为常用。但是,由于两个数据集均需经过MapReduce的shuffle过程,所以reduce端连接的效率往往要低一些。基本思路是mapper为各个记录标记源,并且使用连接件作为map输出键,使键相同的记录放在同一reducer中。
需要使用以下技术

1.多输入

数据集的输入源往往有多中格式,因此可以使用MultipleInputs类来方便地解析和标注各个源。

2.辅助排序

reducer将从两个源中选出键相同的记录且并不介意这些记录是否已排好序。此外,为了更好的执行连接操作,先将某一个源的数据传输到reducer会非常重要。

举个例子

现有气象站文件及气象数据文件,需要将两个文件进行关联

气象站文件内容如下

00001,北京
00002,天津
00003,山东

气象数据文件内容如下

00001,20180101,15
00001,20180102,16
00002,20180101,25
00002,20180102,26
00003,20180101,35
00003,20180102,36

 要求:输出气象站ID 气象站名称及气象数据

代码如下

1.JoinRecordWithStationName类
package com.zhen.mapreduce.join;

import java.io.IOException;
import java.util.Iterator; import org.apache.hadoop.conf.Configured;
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.Partitioner;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.MultipleInputs;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; /**
* @author FengZhen
* @date 2018年9月16日
*
*/
public class JoinRecordWithStationName extends Configured implements Tool{ /**
* 在reduce端连接中,标记气象站记录的mapper
* @author FengZhen
* 00001,北京
00002,天津
00003,山东
*/
static class JoinStationMapper extends Mapper<LongWritable, Text, TextPair, Text>{
private NcdcStationMetadataParser parser = new NcdcStationMetadataParser();
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, TextPair, Text>.Context context)
throws IOException, InterruptedException {
if (parser.parse(value.toString())) {
context.write(new TextPair(parser.getStationId(), "0"), new Text(parser.getStationName()));
}
}
} /**
* 在reduce端连接中标记天气记录的mapper
* @author FengZhen
* 00001,20180101,15
00001,20180102,16
00002,20180101,25
00002,20180102,26
00003,20180101,35
00003,20180102,36
*/
static class JoinRecordMapper extends Mapper<LongWritable, Text, TextPair, Text> {
private NcdcRecordParser parser = new NcdcRecordParser();
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, TextPair, Text>.Context context)
throws IOException, InterruptedException {
parser.parse(value.toString());
context.write(new TextPair(parser.getStationId(), "1"), value);
}
} /**
* reducer知道自己会先接收气象站记录。因此从中抽取出值,并将其作为后续每条输出记录的一部分写到输出文件。
* @author FengZhen
*
*/
static class JoinReducer extends Reducer<TextPair, Text, Text, Text> {
@Override
protected void reduce(TextPair key, Iterable<Text> values, Reducer<TextPair, Text, Text, Text>.Context context)
throws IOException, InterruptedException {
Iterator<Text> iterator = values.iterator();
//取气象站名
Text stationName = new Text(iterator.next());
while (iterator.hasNext()) {
Text record = iterator.next();
Text outValue = new Text(stationName.toString() + "\t" + record.toString());
context.write(key.getFirst(), outValue);
}
}
} static class KeyPartitioner extends Partitioner<TextPair, Text>{
@Override
public int getPartition(TextPair key, Text value, int numPartitions) {
return (key.getFirst().hashCode() & Integer.MAX_VALUE) % numPartitions;
}
} public int run(String[] args) throws Exception {
Job job = Job.getInstance(getConf());
job.setJobName("JoinRecordWithStationName");
job.setJarByClass(JoinRecordWithStationName.class); Path ncdcInputPath = new Path(args[0]);
Path stationInputPath = new Path(args[1]);
Path outputPath = new Path(args[2]); MultipleInputs.addInputPath(job, ncdcInputPath, TextInputFormat.class, JoinRecordMapper.class);
MultipleInputs.addInputPath(job, stationInputPath, TextInputFormat.class, JoinStationMapper.class);
FileOutputFormat.setOutputPath(job, outputPath); job.setPartitionerClass(KeyPartitioner.class);
job.setGroupingComparatorClass(TextPair.FirstComparator.class); job.setMapOutputKeyClass(TextPair.class); job.setReducerClass(JoinReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
return job.waitForCompletion(true) ? 0 : 1;
} public static void main(String[] args) {
String[] params = new String[] {
"hdfs://fz/user/hdfs/MapReduce/data/join/JoinRecordWithStationName/input/record",
"hdfs://fz/user/hdfs/MapReduce/data/join/JoinRecordWithStationName/input/station",
"hdfs://fz/user/hdfs/MapReduce/data/join/JoinRecordWithStationName/output"};
int exitCode = 0;
try {
exitCode = ToolRunner.run(new JoinRecordWithStationName(), params);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(exitCode);
} }

2.NcdcRecordParser类

package com.zhen.mapreduce.join;

import java.io.Serializable;

/**
* @author FengZhen
* @date 2018年9月9日
* 解析天气数据
*/
public class NcdcRecordParser implements Serializable{ private static final long serialVersionUID = 1L; /**
* 气象台ID
*/
private String stationId;
/**
* 时间
*/
private long timeStamp;
/**
* 气温
*/
private Integer temperature; /**
* 解析
* @param value
*/
public void parse(String value) {
String[] values = value.split(",");
if (values.length >= 3) {
stationId = values[0];
timeStamp = Long.parseLong(values[1]);
temperature = Integer.valueOf(values[2]);
}
} /**
* 校验是否合格
* @return
*/
public boolean isValidTemperature() {
return null != temperature;
} public String getStationId() {
return stationId;
} public void setStationId(String stationId) {
this.stationId = stationId;
} public long getTimeStamp() {
return timeStamp;
} public void setTimeStamp(long timeStamp) {
this.timeStamp = timeStamp;
} public Integer getTemperature() {
return temperature;
} public void setTemperature(Integer temperature) {
this.temperature = temperature;
} }

 3.NcdcStationMetadataParser类

package com.zhen.mapreduce.join;

import java.io.Serializable;

/**
* @author FengZhen
* @date 2018年9月9日
* 解析气象台数据
*/
public class NcdcStationMetadataParser implements Serializable{ private static final long serialVersionUID = 1L; /**
* 气象台ID
*/
private String stationId;
/**
* 气象台名称
*/
private String stationName; /**
* 解析
* @param value
*/
public boolean parse(String value) {
String[] values = value.split(",");
if (values.length >= 2) {
stationId = values[0];
stationName = values[1];
return true;
}
return false;
} public String getStationId() {
return stationId;
} public void setStationId(String stationId) {
this.stationId = stationId;
} public String getStationName() {
return stationName;
} public void setStationName(String stationName) {
this.stationName = stationName;
}
}

 4.TextPair类

package com.zhen.mapreduce.join;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException; import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator; /**
* @author FengZhen
* @date 2018年9月16日
*
*/
public class TextPair implements WritableComparable<TextPair>{ private Text first;
private Text second;
public TextPair() {
set(new Text(), new Text());
}
public TextPair(String first, String second) {
set(new Text(first), new Text(second));
}
public TextPair(Text first, Text second) {
set(first, second);
}
public void set(Text first, Text second) {
this.first = first;
this.second = second;
} public void write(DataOutput out) throws IOException {
first.write(out);
second.write(out);
} public void readFields(DataInput in) throws IOException {
first.readFields(in);
second.readFields(in);
} @Override
public int hashCode() {
return first.hashCode() * 163 + second.hashCode();
} @Override
public boolean equals(Object obj) {
if (obj instanceof TextPair) {
TextPair textPair = (TextPair) obj;
return first.equals(textPair.first) && second.equals(textPair.second);
}
return false;
} public int compareTo(TextPair o) {
int cmp = first.compareTo(o.first);
if (cmp != 0) {
return cmp;
}
return second.compareTo(o.second);
} public Text getFirst() {
return first;
}
public void setFirst(Text first) {
this.first = first;
}
public Text getSecond() {
return second;
}
public void setSecond(Text second) {
this.second = second;
}
@Override
public String toString() {
return first + "\t" + second;
} /**
* 比较两个int值大小
* 降序
* @param a
* @param b
* @return
*/
public static int compare(Text a, Text b) {
return a.compareTo(b);
} static class FirstComparator extends WritableComparator{
protected FirstComparator() {
super(TextPair.class, true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
TextPair ip1 = (TextPair) a;
TextPair ip2 = (TextPair) b;
return TextPair.compare(ip1.getFirst(), ip2.getFirst());
}
} }

 打jar包,上传并执行

scp /Users/FengZhen/Desktop/Hadoop/file/JoinRecordWithStationName.jar root@192.168.1.124:/usr/local/test/mr
hadoop jar JoinRecordWithStationName.jar com.zhen.mapreduce.join.JoinRecordWithStationName

 结果如下

00001	北京	00001,20180102,16
00001 北京 00001,20180101,15
00002 天津 00002,20180102,26
00002 天津 00002,20180101,25
00003 山东 00003,20180102,36
00003 山东 00003,20180101,35

 


MapReduce-join连接的更多相关文章

  1. mapreduce join

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

  2. 一起学Hive——总结各种Join连接的用法

    Hive支持常用的SQL join语句,例如内连接.左外连接.右外连接以及HiVe独有的map端连接.其中map端连接是用于优化Hive连接查询的一个重要技巧. 在介绍各种连接之前,先准备好表和数据. ...

  3. CROSS JOIN连接用于生成两张表的笛卡尔集

    将两张表的情况全部列举出来 结果表: 列= 原表列数相加 行= 原表行数相乘     CROSS JOIN连接用于生成两张表的笛卡尔集. 在sql中cross join的使用: 1.返回的记录数为两个 ...

  4. 数据库(学习整理)----7--Oracle多表查询,三种join连接

    聚合函数:(都会忽略null数据) 常用的有5种:将字段中所有的数据聚合在一条中 .sum(字段名) :求总和 .avg(字段名) :求平均值 .max(字段名) :求最大值 .min(字段名) :求 ...

  5. 左连接LEFT JOIN 连接自己时的查询结果测试

    #左连接LEFT JOIN 连接自己时的查询结果测试 #左连接LEFT JOIN 连接自己时的查询结果(都会出现两个重复字段),两个表都有as后只能查询相等条件merchant_shop_id非nul ...

  6. 【SQL】各取所需 | SQL JOIN连接查询各种用法总结

    前面 在实际应用中,大多的查询都是需要多表连接查询的,但很多初学SQL的小伙伴总对各种JOIN有些迷糊.回想一下,初期很长一段时间,我常用的似乎也就是等值连接 WHERE 后面加等号,对各种JOIN也 ...

  7. 图解 5 种 Join 连接及实战案例!(inner/ left/ right/ full/ cross)

    Join 连接在日常开发用得比较多,但大家都搞清楚了它们的使用区别吗??一文带你上车~~ 内连接 inner join 内连接是基于连接谓词将俩张表(如A和B)的列组合到一起产生新的结果表,在表中存在 ...

  8. UNION JOIN 连接表

    使用UNION JOIN进行多表连接,与9.3节介绍的各种表的连接类型不同,它并不对表中的数据进行任何匹配处理,而只是把来自一个源表中的行与另一个源表中的行联合起来,生成的结果表中包括第一个表中的所有 ...

  9. MySQL之表、列别名及各种JOIN连接详解

    MySQL在SQL中,合理的别名可以让SQL更容易以及可读性更高.别名使用as来表示,可以分为表别名和列别名,别名应该是先定义后使用才对,所以首先要了解sql的执行顺序(1) from(2) on(3 ...

  10. MapReduce数据连接

    对于不同文件里的数据,有时候有相应关系,须要进行连接(join),获得一个新的文件以便进行分析.比方有两个输入文件a.txt,b.txt,当中的数据格式分别例如以下 1 a 2 b 3 c 4 d 1 ...

随机推荐

  1. Lucene建立索引搜索入门实例

                                第一部分:Lucene建立索引 Lucene建立索引主要有以下两步:第一步:建立索引器第二步:添加索引文件准备在f盘建立lucene文件夹,然后 ...

  2. Android开发:《Gradle Recipes for Android》阅读笔记(翻译)3.4——Flavor Dimensions

    问题: 一个product flavor不够,你需要另一个标准去区分不同版本的app 解决方案: 在product flavor中增加flavorDimensions 讨论: 在3.2章展示了一个有三 ...

  3. JavaScript的BOM和DOM

    JavaScript的BOM和DOM 1,window对象,所有浏览器都支持window对象,它表示浏览器窗口 BOM(browser Object Model)是指浏览器对象模型,它使JavaScr ...

  4. 【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)

    [BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交 ...

  5. 【BZOJ4325】NOIP2015 斗地主 搜索+剪枝

    [BZOJ4325]NOIP2015 斗地主 Description 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗 ...

  6. Yue Fei's Battle(组合计数递推)

    //求一个直径为 k 的树有多少种形态,每个点的度不超过 3 // 非常完美的分析,学到了,就是要细细推,并且写的时候要细心 还有除法取模需要用逆元 #include <iostream> ...

  7. Python中MRO

    MRO(方法解析顺序) 当有多重继承时,基于“从左到右,深度优先原则”: class CommonBase(): def Method(self): print('CommonBase') class ...

  8. angularjs 复选框 单选框

    关于复选框,在做项目的时候,有一下几点心得 单选框 1.判断哪个单选框选中的情况 html代码 判断该复选框是否选中 $scope.agree.isChecked     判断这个值,如果等于1,代表 ...

  9. grafana-----Templating

    模板允许更多的互动和动态的仪表板.可以将变量用在度量查询中,不必硬编码诸如服务器.应用程序和传感器名称之类的东西.变量显示在仪表板顶部的下拉式选择框中.这些下拉菜单可以很容易地改变在你的仪表板显示的数 ...

  10. Python操作Redis(一)

    redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...