MapReduce-join连接
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连接的更多相关文章
- mapreduce join
MapReduce Join 对两份数据data1和data2进行关键词连接是一个很通用的问题,如果数据量比较小,可以在内存中完成连接. 如果数据量比较大,在内存进行连接操会发生OOM.mapredu ...
- 一起学Hive——总结各种Join连接的用法
Hive支持常用的SQL join语句,例如内连接.左外连接.右外连接以及HiVe独有的map端连接.其中map端连接是用于优化Hive连接查询的一个重要技巧. 在介绍各种连接之前,先准备好表和数据. ...
- CROSS JOIN连接用于生成两张表的笛卡尔集
将两张表的情况全部列举出来 结果表: 列= 原表列数相加 行= 原表行数相乘 CROSS JOIN连接用于生成两张表的笛卡尔集. 在sql中cross join的使用: 1.返回的记录数为两个 ...
- 数据库(学习整理)----7--Oracle多表查询,三种join连接
聚合函数:(都会忽略null数据) 常用的有5种:将字段中所有的数据聚合在一条中 .sum(字段名) :求总和 .avg(字段名) :求平均值 .max(字段名) :求最大值 .min(字段名) :求 ...
- 左连接LEFT JOIN 连接自己时的查询结果测试
#左连接LEFT JOIN 连接自己时的查询结果测试 #左连接LEFT JOIN 连接自己时的查询结果(都会出现两个重复字段),两个表都有as后只能查询相等条件merchant_shop_id非nul ...
- 【SQL】各取所需 | SQL JOIN连接查询各种用法总结
前面 在实际应用中,大多的查询都是需要多表连接查询的,但很多初学SQL的小伙伴总对各种JOIN有些迷糊.回想一下,初期很长一段时间,我常用的似乎也就是等值连接 WHERE 后面加等号,对各种JOIN也 ...
- 图解 5 种 Join 连接及实战案例!(inner/ left/ right/ full/ cross)
Join 连接在日常开发用得比较多,但大家都搞清楚了它们的使用区别吗??一文带你上车~~ 内连接 inner join 内连接是基于连接谓词将俩张表(如A和B)的列组合到一起产生新的结果表,在表中存在 ...
- UNION JOIN 连接表
使用UNION JOIN进行多表连接,与9.3节介绍的各种表的连接类型不同,它并不对表中的数据进行任何匹配处理,而只是把来自一个源表中的行与另一个源表中的行联合起来,生成的结果表中包括第一个表中的所有 ...
- MySQL之表、列别名及各种JOIN连接详解
MySQL在SQL中,合理的别名可以让SQL更容易以及可读性更高.别名使用as来表示,可以分为表别名和列别名,别名应该是先定义后使用才对,所以首先要了解sql的执行顺序(1) from(2) on(3 ...
- MapReduce数据连接
对于不同文件里的数据,有时候有相应关系,须要进行连接(join),获得一个新的文件以便进行分析.比方有两个输入文件a.txt,b.txt,当中的数据格式分别例如以下 1 a 2 b 3 c 4 d 1 ...
随机推荐
- Harmonic Value Description(构造题)
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submission ...
- 九度OJ 1360:乐透之猜数游戏 (递归)
时间限制:2 秒 内存限制:32 兆 特殊判题:否 提交:955 解决:261 题目描述: 六一儿童节到了,YZ买了很多丰厚的礼品,准备奖励给JOBDU里辛劳的员工.为了增添一点趣味性,他还准备了一些 ...
- Js slice()方法和splice()方法
1.slice(start,end) 从已有的数组中返回选定元素,参数start必填,end选填 <script> delArray(); function delArray(){ var ...
- dataTables的导出Excel功能
Datatables它是一款基于jQuery表格插件,钟情于它操作dom的灵活.做后台的同学想必使用它能事半功倍,而且交互强.容易扩展. 我也是最近要做公司后台界面,表格涉及的很多,所以考虑使用DT, ...
- 洛谷 P1407 [国家集训队]稳定婚姻
洛谷 这个题面很有意思,像我这样的菜鸡,完全不需考虑婚姻的稳定 性 问题. tarjan裸题,直接讲算法吧: 原配夫妻之间分别连一条边,小情人之间反向连边. 这时候我们会发现一个性质,如果婚姻稳定,那 ...
- web测试策略
一.输入框 二.搜索功能 三.添加功能 四.修改功能 五.删除功能 六.注册.登陆模块 七.上传图片测试 八:文件导出 九.文件下载页面 十.查询结果列表 十一.cookie 一.输入框 1 字 ...
- (4.11)sql server内存使用
一些内存使用错误理解 开篇小感悟 在实际的场景中会遇到各种奇怪的问题,为什么会感觉到奇怪,因为没有理论支撑的东西才感觉到奇怪,SQL Server自己管理内存,我们可以干预的方式也很少,所以日常很 ...
- android学习一---搭建开发环境
android基于Java并运行Linux内核上的轻量级操作系统.由于是基于java的,学习起来也不是太难,对java有一定了解并知道一些基本的图形用户界面,入门就很简单了. 一.了解JDK ,SDK ...
- Win10在右键菜单添加“在此处打开命令窗口”设置项
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Directory\shell\OpenCmdHere] @="在此处打开命令 ...
- github代码托管
下载github客户端软件 1) 官网下载help.github.com 2) 百度搜索,一般用于windows7以前的系统 安装github软件 按照软件提示安装即可.不过,博主倾向使用命令行工 ...