MapReduce Join关联
Reduce join
原理
Map端的主要工作:为来自不同表(文件)的key/value对打标签以区别不同来源的记录。然后用连接字段作为key,其余部分和新加的标志作为value,最后进行输出。
Reduce端的主要工作:在reduce端以连接字段作为key的分组已经完成,我们只需要在每一个分组当中将那些来源于不同文件的记录(在map阶段已经打标志)分开,最后进行合并就ok了
需求
订单数据表t_order
id pid amount 商品信息表t_product
pid pname
小米
华为
格力 最终数据形式
id pname amount
小米
小米
华为
华为
格力
格力
缺点
缺点:这种方式中,合并的操作是在reduce阶段完成,reduce端的处理压力太大,map节点的运算负载则很低,资源利用率不高,且在reduce阶段极易产生数据倾斜(同一个reduce接收到的数据量很大)
解决方案: map端实现数据合并
案例
package com.bigdata.mapreduce.table;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable; public class TableBean implements Writable {
private String order_id; // 订单id
private String p_id; // 产品id
private int amount; // 产品数量
private String pname; // 产品名称
private String flag;// 表的标记 public TableBean() {
super();
} public TableBean(String order_id, String p_id, int amount, String pname, String flag) {
super();
this.order_id = order_id;
this.p_id = p_id;
this.amount = amount;
this.pname = pname;
this.flag = flag;
} public String getFlag() {
return flag;
} public void setFlag(String flag) {
this.flag = flag;
} public String getOrder_id() {
return order_id;
} public void setOrder_id(String order_id) {
this.order_id = order_id;
} public String getP_id() {
return p_id;
} public void setP_id(String p_id) {
this.p_id = p_id;
} public int getAmount() {
return amount;
} public void setAmount(int amount) {
this.amount = amount;
} public String getPname() {
return pname;
} public void setPname(String pname) {
this.pname = pname;
} @Override
public void write(DataOutput out) throws IOException {
out.writeUTF(order_id);
out.writeUTF(p_id);
out.writeInt(amount);
out.writeUTF(pname);
out.writeUTF(flag);
} @Override
public void readFields(DataInput in) throws IOException {
this.order_id = in.readUTF();
this.p_id = in.readUTF();
this.amount = in.readInt();
this.pname = in.readUTF();
this.flag = in.readUTF();
} @Override
public String toString() {
return order_id + "\t" + pname + "\t" + amount + "\t" ;
}
}
2)编写TableMapper程序
package com.bigdata.mapreduce.table;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileSplit; public class TableMapper extends Mapper<LongWritable, Text, Text, TableBean>{
TableBean bean = new TableBean();
Text k = new Text(); @Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException { // 1 获取输入文件类型
FileSplit split = (FileSplit) context.getInputSplit();
String name = split.getPath().getName(); // 2 获取输入数据
String line = value.toString(); // 3 不同文件分别处理
if (name.startsWith("order")) {// 订单表处理
// 3.1 切割
String[] fields = line.split("\t"); // 3.2 封装bean对象
bean.setOrder_id(fields[0]);
bean.setP_id(fields[1]);
bean.setAmount(Integer.parseInt(fields[2]));
bean.setPname("");
bean.setFlag("0"); k.set(fields[1]);
}else {// 产品表处理
// 3.3 切割
String[] fields = line.split("\t"); // 3.4 封装bean对象
bean.setP_id(fields[0]);
bean.setPname(fields[1]);
bean.setFlag("1");
bean.setAmount(0);
bean.setOrder_id(""); k.set(fields[0]);
}
// 4 写出
context.write(k, bean);
}
}
3)编写TableReducer程序
package com.bigdata.mapreduce.table;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer; public class TableReducer extends Reducer<Text, TableBean, TableBean, NullWritable> { @Override
protected void reduce(Text key, Iterable<TableBean> values, Context context)
throws IOException, InterruptedException { // 1准备存储订单的集合
ArrayList<TableBean> orderBeans = new ArrayList<>();
// 2 准备bean对象
TableBean pdBean = new TableBean(); for (TableBean bean : values) { if ("0".equals(bean.getFlag())) {// 订单表
// 拷贝传递过来的每条订单数据到集合中
TableBean orderBean = new TableBean();
try {
BeanUtils.copyProperties(orderBean, bean);
} catch (Exception e) {
e.printStackTrace();
} orderBeans.add(orderBean);
} else {// 产品表
try {
// 拷贝传递过来的产品表到内存中
BeanUtils.copyProperties(pdBean, bean);
} catch (Exception e) {
e.printStackTrace();
}
}
} // 3 表的拼接
for(TableBean bean:orderBeans){
bean.setPname (pdBean.getPname());
// 4 数据写出去
context.write(bean, NullWritable.get());
}
}
}
4)编写TableDriver程序
package com.bigdata.mapreduce.table;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class TableDriver { public static void main(String[] args) throws Exception {
// 1 获取配置信息,或者job对象实例
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration); // 2 指定本程序的jar包所在的本地路径
job.setJarByClass(TableDriver.class); // 3 指定本业务job要使用的mapper/Reducer业务类
job.setMapperClass(TableMapper.class);
job.setReducerClass(TableReducer.class); // 4 指定mapper输出数据的kv类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(TableBean.class); // 5 指定最终输出的数据的kv类型
job.setOutputKeyClass(TableBean.class);
job.setOutputValueClass(NullWritable.class); // 6 指定job的输入原始文件所在目录
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); // 7 将job中配置的相关参数,以及job所用的java类所在的jar包, 提交给yarn去运行
boolean result = job.waitForCompletion(true);
System.exit(result ? 0 : 1);
}
}
Map join
使用场景
一张表十分小、一张表很大。
解决方案
在map端缓存多张表,提前处理业务逻辑,这样增加map端业务,减少reduce端数据的压力,尽可能的减少数据倾斜。
具体办法
(1)在mapper的setup阶段,将文件读取到缓存集合中。
(2)在驱动函数中加载缓存。
job.addCacheFile(new URI("file:/e:/mapjoincache/pd.txt"));// 缓存普通文件到task运行节点
案例
package test;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class DistributedCacheDriver { public static void main(String[] args) throws Exception {
// 1 获取job信息
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration); // 2 设置加载jar包路径
job.setJarByClass(DistributedCacheDriver.class); // 3 关联map
job.setMapperClass(DistributedCacheMapper.class);
// 4 设置最终输出数据类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(NullWritable.class); // 5 设置输入输出路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); // 6 加载缓存数据
job.addCacheFile(new URI("file:///e:/inputcache/pd.txt")); // 7 map端join的逻辑不需要reduce阶段,设置reducetask数量为0
job.setNumReduceTasks(0); // 8 提交
boolean result = job.waitForCompletion(true);
System.exit(result ? 0 : 1);
}
}
(2)读取缓存的文件数据
package test;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper; public class DistributedCacheMapper extends Mapper<LongWritable, Text, Text, NullWritable>{ Map<String, String> pdMap = new HashMap<>(); @Override
protected void setup(Mapper<LongWritable, Text, Text, NullWritable>.Context context)
throws IOException, InterruptedException { // 1 获取缓存的文件
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("pd.txt"),"UTF-8")); String line;
while(StringUtils.isNotEmpty(line = reader.readLine())){
// 2 切割
String[] fields = line.split("\t"); // 3 缓存数据到集合
pdMap.put(fields[0], fields[1]);
} // 4 关流
reader.close();
} Text k = new Text(); @Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
// 1 获取一行
String line = value.toString(); // 2 截取
String[] fields = line.split("\t"); // 3 获取产品id
String pId = fields[1]; // 4 获取商品名称
String pdName = pdMap.get(pId); // 5 拼接
k.set(line + "\t"+ pdName); // 6 写出
context.write(k, NullWritable.get());
}
}
MapReduce Join关联的更多相关文章
- JOIN关联表中ON,WHERE后面跟条件的区别
select * from td left join (select case_id as sup_case_id , count(*) supervise_number from td_kcdc ...
- oracle使用LEFT JOIN关联产生的问题在查询结果中使用CASE WHEN 无法判断
oracle使用LEFT JOIN关联产生的问题在查询结果中使用CASE WHEN 无法判断 查询方式一: SELECT CASE WHEN (SELECT CAST(SUM(CASE ) THEN ...
- mapreduce join
MapReduce Join 对两份数据data1和data2进行关键词连接是一个很通用的问题,如果数据量比较小,可以在内存中完成连接. 如果数据量比较大,在内存进行连接操会发生OOM.mapredu ...
- PLSQL_性能优化系列02_Oracle Join关联
2014-09-25 Created By BaoXinjian
- SQL之Left Join 关联条件的探讨
在测试工作中,有时需要测试数据库数据经过sql计算后的结果是否满足某一功能查询得到的返回值. 针对某些需要功能需要联查多张表,此时 关联 的作用就异常重要了,而针对多表关联,其中 关联条件的重要性不言 ...
- 【Oracle】两个表Join关联更新
两个表关联,用B表的字段更新A表的字段. UPDATE ( SELECT A.COL1 A_COL, B.COL2 B_COL FROM table1 A INNER JOIN table2 B ON ...
- 深入理解mysql的自连接和join关联
一.mysql自连接 mysql有时在信息查询时需要进行对自身连接(自连接),所以我们需要为表定义别名.我们举例说明,下面是商品采购表,我们需要找到采购价格比惠惠高的所有信息. 一般情况我们看到这张表 ...
- laravel4.2 union联合,join关联分组查询最新记录时,查询条件不对,解决方案
需求: 分组联合查询,或者最新记录. 问题: mysql分组的时候默认会查询第一条记录,存在gourp by时 order by 无效. 一般解决办法就是 ,select * from ( sele ...
- SQL join中级篇--hive中 mapreduce join方法分析
1. 概述. 本文主要介绍了mapreduce框架上如何实现两表JOIN. 2. 常见的join方法介绍 假设要进行join的数据分别来自File1和File2. 2.1 reduce side jo ...
随机推荐
- 树的点分治 板题 Luogu P3806
给定一棵有n个点的树 询问树上距离为k的点对是否存在. AC code: #include<bits/stdc++.h> using namespace std; const int MA ...
- Centos7 修改为阿里的yum源
1.备份本地yum源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo_bak 2.获取阿里yum源配置文件 ...
- robot framework的使用方法
1.后台代码: 目录结构: 测试代码:Arithmetic.py 2.开始编写用例 直接在eclipse上新建一个txt文件即可,或者是通过ride编写用例. (1).首先在eclipse上新建目录T ...
- [Javascript] Keyword 'in' to check prop exists on Object
function addTo80(n ) { + n; } function memoizedAddTo80 (fn) { let cache = {}; return (n) => { /*k ...
- jar包更新
打包新的jar包 java -jar xx.jar 本地测试后 删除旧的jar包 然后要重启jar包
- Android Studio一直显示Building“project name”Gradle project info问题详解
关注我,每天都有优质技术文章推送,工作,学习累了的时候放松一下自己. 本篇文章同步微信公众号 欢迎大家关注我的微信公众号:「醉翁猫咪」 Android Studio一直显示 Building&quo ...
- Clion下同时编写多个main函数
在你的CMakeLists.txt文件下配置,使用add_executable(),前面的一定要不一样 红色部分是描述main的,配置后运行处可以选择:
- windows 共享文件夹
windows 共享文件夹 同步工作组 右键单击"计算机",选择"属性" 更改设置 单击"更改". 输入工作组 和 主机名 启计算机使更改生 ...
- Compute API 关键概念 详解
Compute API 是 RESTful HTTP 服务,提供管理虚机的能力. 虚机可能有不同的内存大小,CPU数量,硬盘大小,能够在几分钟之内创建出来.和虚机的交互,可以通过Compute API ...
- DATEADD (Transact-SQL)
DATEADD (Transact-SQL) This function adds a specified number value (as a signed integer) to a specif ...