需求分析

如下两张输入表格

order

id pid amount
1001 01 1
1002 02 2
1003 03 3
1004 01 4
1005 02 5
1006 03 6

pd

pid pname
01 小米
02 华为
03 格力

将商品信息表中数据根据商品pid合并的订单数据表中。原文:sw-code

id pname amount
1001 小米 1
1004 小米 4
1002 华为 2
1005 华为 5
1003 格力 3
1006 格力 6

Reduce Join

创建一个TableBean对象,其包含两个文件的所有属性,方便在map阶段封装数据

public class TableBean implements Writable {

    private String id;
private String pid;
private Integer amount;
private String pname;
private String flag; public TableBean() {
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getPid() {
return pid;
} public void setPid(String pid) {
this.pid = pid;
} public Integer getAmount() {
return amount;
} public void setAmount(Integer amount) {
this.amount = amount;
} public String getPname() {
return pname;
} public void setPname(String pname) {
this.pname = pname;
} public String getFlag() {
return flag;
} public void setFlag(String flag) {
this.flag = flag;
} @Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeUTF(id);
dataOutput.writeUTF(pid);
dataOutput.writeInt(amount);
dataOutput.writeUTF(pname);
dataOutput.writeUTF(flag);
} @Override
public void readFields(DataInput dataInput) throws IOException {
this.id = dataInput.readUTF();
this.pid = dataInput.readUTF();
this.amount = dataInput.readInt();
this.pname = dataInput.readUTF();
this.flag = dataInput.readUTF();
} @Override
public String toString() {
return id + '\t' + pname + '\t' + amount;
}
}

在map阶段根据文件名来区分加载对象,setup方法一个文件只会执行一次,在该方法中获取文件名称,在map方法中根据文件名来执行不同的操作,值得注意的是属性不能为默认的NULL

public class TableMapper extends Mapper<LongWritable, Text, Text, TableBean> {

    private String filename;
private Text outK = new Text();
private TableBean outV = new TableBean();
@Override
protected void setup(Mapper<LongWritable, Text, Text, TableBean>.Context context) throws IOException, InterruptedException {
// 初始化
FileSplit inputSplit = (FileSplit) context.getInputSplit();
filename = inputSplit.getPath().getName();
} @Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, TableBean>.Context context) throws IOException, InterruptedException {
String line = value.toString();
//判断是哪个文件
if (filename.contains("order")) {
String[] split = line.split("\t");
// 封装k v
outK.set(split[1]);
outV.setId(split[0]);
outV.setPid(split[1]);
outV.setAmount(Integer.parseInt(split[2]));
outV.setPname("");
outV.setFlag("order");
} else {
String[] split = line.split("\t");
// 封装k v
outK.set(split[0]);
outV.setId("");
outV.setPid(split[0]);
outV.setAmount(0);
outV.setPname(split[1]);
outV.setFlag("pd");
}
//写出
context.write(outK, outV);
}
}

由于使用pidkey,两个表中相同的pid会进入同一个reduce,再根据flag判断是哪个表中的数据,如果是order将其保存到数组中,如果是pd则获取其pname,循环order数组赋值。值得注意的是,由于values并非Java中默认的迭代器,如果只是add(value)赋值的是地址,无法达到预期要求。

public class TableReducer extends Reducer<Text, TableBean, TableBean, NullWritable> {

    @Override
protected void reduce(Text key, Iterable<TableBean> values, Reducer<Text, TableBean, TableBean, NullWritable>.Context context) throws IOException, InterruptedException {
ArrayList<TableBean> orderBeans = new ArrayList<>();
TableBean pBean = new TableBean();
for (TableBean value : values) {
if ("order".equals(value.getFlag())) {
TableBean tempTableBean = new TableBean();
try {
BeanUtils.copyProperties(tempTableBean, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
orderBeans.add(tempTableBean);
} else {
try {
BeanUtils.copyProperties(pBean, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
// 遍历orderBeans
for (TableBean orderBean : orderBeans) {
orderBean.setPname(pBean.getPname());
context.write(orderBean, NullWritable.get());
} }
}

总结:如果数据量非常大,所有的压力都会来到reduce阶段,这样会导致数据倾斜。为了防止发生,可以将Join操作放到map阶段,因为map阶段处理的数据都是块大小128M

Map Join

Map Join适用与一张十分小、一张很大的表的场景

在Map端缓存多张表,提前处理业务逻辑,这样增加Map端业务,减少Reduce端数据的压力,尽可能的减少数据倾斜。

采用DistributedCache的方法:

(1)在Mapper的setup阶段,将文件读取到缓存集合中

(2)在Driver驱动类中加载缓存

// 缓存普通文件到Task运行节点
job.addCacheFile(new URI("file:///e:/cache/pd.txt"));
// 如果是集群运行,需要设置HDFS路径
job.addCacheFile(new URI("hdfs://hadoop102:8020/cache/pd.txt"));

实操案例

Mapper
public class MapJoinMapper extends Mapper<LongWritable, Text, Text, NullWritable> {

    private HashMap<String, String> pdMap = new HashMap<>();
private Text outK = new Text(); @Override
protected void setup(Mapper<LongWritable, Text, Text, NullWritable>.Context context) throws IOException, InterruptedException {
// 获取缓存文件,并把文件内容封装到集合中 pd.txt
URI[] cacheFiles = context.getCacheFiles();
URI cacheFile = cacheFiles[0];
FileSystem fs = FileSystem.get(context.getConfiguration());
FSDataInputStream fis = fs.open(new Path(cacheFile)); // 从流中读取数据
BufferedReader reader = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
String line;
while (StringUtils.isNotEmpty(line=reader.readLine())) {
// 切割
String[] fields = line.split("\t");
pdMap.put(fields[0], fields[1]);
}
IOUtils.closeStream(reader);
} @Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, NullWritable>.Context context) throws IOException, InterruptedException { // 处理 order.txt
String line = value.toString();
String[] split = line.split("\t");
String pName = pdMap.get(split[1]); outK.set(split[0] + "\t" + pName + "\t" + split[2]);
context.write(outK, NullWritable.get());
}
}
Driver
public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException, ClassNotFoundException {
Job job = Job.getInstance(new Configuration());
job.setMapperClass(MapJoinMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(NullWritable.class); job.setOutputKeyClass(Text.class);
job.setOutputValueClass(NullWritable.class); job.addCacheFile(new URI("file:///D:/hadoop/input/mapjoincache/pd.txt"));
// 不需要reduce阶段
job.setNumReduceTasks(0); FileInputFormat.setInputPaths(job, new Path("D:\\hadoop\\input\\mapjoin"));
FileOutputFormat.setOutputPath(job, new Path("D:\\hadoop\\output\\mapjoin")); boolean b = job.waitForCompletion(true);
System.exit(b?0:1);
}
}

下篇文章:

相关文章:

大数据之Hadoop集群中Yarn常用命令

大数据之Hadoop集群的HDFS压力测试

大数据之Hadoop集群中MapReduce的Join操作的更多相关文章

  1. 大数据测试之hadoop集群配置和测试

    大数据测试之hadoop集群配置和测试   一.准备(所有节点都需要做):系统:Ubuntu12.04java版本:JDK1.7SSH(ubuntu自带)三台在同一ip段的机器,设置为静态IP机器分配 ...

  2. 大数据学习——HADOOP集群搭建

    4.1 HADOOP集群搭建 4.1.1集群简介 HADOOP集群具体来说包含两个集群:HDFS集群和YARN集群,两者逻辑上分离,但物理上常在一起 HDFS集群: 负责海量数据的存储,集群中的角色主 ...

  3. 大数据平台Hadoop集群搭建

    一.概念 Hadoop是由java语言编写的,在分布式服务器集群上存储海量数据并运行分布式分析应用的开源框架,其核心部件是HDFS与MapReduce.HDFS是一个分布式文件系统,类似mogilef ...

  4. Java+大数据开发——Hadoop集群环境搭建(一)

    1集群简介 HADOOP集群具体来说包含两个集群:HDFS集群和YARN集群,两者逻辑上分离,但物理上常在一起 HDFS集群: 负责海量数据的存储,集群中的角色主要有 NameNode / DataN ...

  5. Java+大数据开发——Hadoop集群环境搭建(二)

    1. MAPREDUCE使用 mapreduce是hadoop中的分布式运算编程框架,只要按照其编程规范,只需要编写少量的业务逻辑代码即可实现一个强大的海量数据并发处理程序 2. Demo开发--wo ...

  6. 大数据之hadoop集群安全模式

    集群安全模式1.概述(1)NameNode启动 NameNode启动时,首先将镜像文件(Fsimage)载入内存,并执行编辑日志(Edits)中的各项操作.-旦在内存中成功建立文件系统元数据的影像,则 ...

  7. 大数据学习——hadoop集群搭建2.X

    1.准备Linux环境 1.0先将虚拟机的网络模式选为NAT 1.1修改主机名 vi /etc/sysconfig/network NETWORKING=yes HOSTNAME=itcast ### ...

  8. CDH构建大数据平台-配置集群的Kerberos认证安全

     CDH构建大数据平台-配置集群的Kerberos认证安全 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 当平台用户使用量少的时候我们可能不会在一集群安全功能的缺失,因为用户少,团 ...

  9. 朝花夕拾之--大数据平台CDH集群离线搭建

    body { border: 1px solid #ddd; outline: 1300px solid #fff; margin: 16px auto; } body .markdown-body ...

  10. Hadoop集群中添加硬盘

    Hadoop工作节点扩展硬盘空间 接到老板任务,Hadoop集群中硬盘空间不够用,要求加一台机器到Hadoop集群,并且每台机器在原有基础上加一块2T硬盘,老板给力啊,哈哈. 这些我把完成这项任务的步 ...

随机推荐

  1. SQL 通配符:用于模糊搜索和匹配的 SQL 关键技巧

    SQL通配符字符 通配符字符用于替代字符串中的一个或多个字符.通配符字符与LIKE运算符一起使用.LIKE运算符用于在WHERE子句中搜索列中的指定模式. 示例 返回所有以字母 'a' 开头的客户: ...

  2. 掌握 Spring IoC 容器与 Bean 作用域:详解 singleton 与 prototype 的使用与配置

    在您的应用程序中,由 Spring IoC 容器管理的形成其核心的对象被称为 "bean".一个 bean 是由 Spring IoC 容器实例化.组装和管理的对象 这些 bean ...

  3. .NET 9 预览版 3 发布

    我们很高兴地宣布发布 .NET 9 预览版 3,其中包含 .NET 库.运行时和 SDK 的新功能和改进.此预览版带来了旨在提高性能.提高开发人员工作效率以及向 .NET 生态系统引入新功能的增强功能 ...

  4. 【转】CentOS安装VMware Tools

    [转]CentOS安装VMware Tools VMware 是非常好的虚拟机软件.如果系统安装了VMware Tools以后对虚拟机的性能会提升很多的.下面是如果在CentOS系统内安装VMware ...

  5. 整理k8s————k8s组件[二]

    前言 简单整理一下k8s 组件. 正文 borg 架构: borgmaster 是处理请求分发的. borglet 是具体运行容器. 这里有一个调度scheduler,这个比较重要吧. 比如说用户通过 ...

  6. jenkins 持续集成和交付——一个构件小栗子前置(三)

    前言 下面介绍构建一个小栗子. 在此之前有个小前提,就是已经安装好了git 服务器,用的是gogs,详细请看外篇. 正文 插件安装 首先你要安装一个git插件. 装完git插件后,我们还得安装一些gi ...

  7. 元素类型 “item” 相关联的 “name” 属性值不能包含 ‘<’ 字符

    Android构建时报错: app:lintVitalRelease[Fatal Error] :3:214: 与元素类型 "item" 相关联的 "name" ...

  8. 跨域是什么?Vue项目中你是如何解决跨域的呢?

    一.跨域是什么 跨域本质是浏览器基于同源策略的一种安全手段 同源策略(Sameoriginpolicy),是一种约定,它是浏览器最核心也最基本的安全功能 所谓同源(即指在同一个域)具有以下三个相同点 ...

  9. 如何将 ASP.NET Core MVC 项目的视图分离到另一个项目

    如何将 ASP.NET Core MVC 项目的视图分离到另一个项目 在当下这个年代 SPA 已是主流,人们早已忘记了 MVC 以及 Razor 的故事.但是在某些场景下 SSR 还是有意想不到效果. ...

  10. 剑指 Offer 58 - II(Java)-左旋转字符串(简单)

    题目: 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部.请定义一个函数实现字符串左旋转操作的功能.比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位 ...