Hadoop实践(三)---高阶MapReduce

时间:2022-10-10 05:11:15

1、链接多个MapReduce作业

  • 通常会存在这样的情况,无法把整个流程写在单个MapReduce作业中。因此,需要将多个MapReduce程序链接成更大的作业
  • 数据处理可能涉及多个数据集,因此需要讨论多个数据集的各种联结技术

1-1、顺序链接MapReduce作业

生成一个自动化的执行序列,将MapReduce作业按照顺序链接在一起,用一个MapReduce作业的输出作为下一个的输入

类似于Linux中的管道命令:

mapreduce1 | mapreduce2 | mapreduce3 | 。。。

顺序链接MapReduce作业很简单, driver类为作业创建一个带有配置参数的job(jobconf旧API)的对象,并将该对象传递给job.submit(jonClient.runJob旧API)来启动这个作业;当运行到作业结尾处被阻止时,作业的链接会在MapReduce作业之后调用另一个作业的driver。(每个作业的driver必须创建一个新的对象,并将输入路径设置为前一个输出路径,可以在最后一个作业中删除前面的中间结果)

1-2、复杂依赖MapReduce作业

复杂的数据处理任务的子任务并不是按顺序运行的,作业不能按照现行方式链接(例如,MR1处理Set1,MR2处理Set2;MR3处理MR1和MR2的结果)

通过Job和JobControl来管理这样的非线性作业一直的相互依赖,采用的是addDependingJob()方法:

A.addDependingJob(B) //A在B完成之前不会启动

通过addJob()方法,可以为JobControl对象添加作业,当所有作业和依赖关系添加完成后,调用JobControl的run()方法生成一个线程来提交作业并监视其执行,JobControl中有一些方法来跟踪批处理中各个作业的执行

1-3、预处理和后处理阶段的链接

数据的处理任务涉及对记录的预处理和后处理:

  1. 可以为预处理和后处理步骤各自编写一个MapReduce作业,并把它们链接起来。这样的话每个步骤的中间结果都需要占用I/O,和存储资源,效率低下。
  2. 另一种方法,编写mapper去预先调用所有的预处理步骤,再让reducer调用所有的后处理步骤,这将强制采用模块化和可组合的方式来构建预处理和后处理的构成【ChainMapper & ChainReducer】,全部步骤在单一的作业中运行,不会产生中间文件,大大减少I/O操作

关于2的ChainMapper & ChainReducer:

MAP | REDUCE   //1
MAP+ | REDUCE |MAP* //2
MAP1 | MAP2 | REDUCE | MAP3 | MAP4 //3
  • 1 的map和reduce可以重复执行一次或多次,一个跟着一个
  • 2 的作业按序执行多个map来预处理,然后执行reducer 然后在执行多个map进行后处理,在ChainMapper & ChainReducer中调用addMapper()方法来分别组合预处理和后处理的步骤
  • 3 的作业可以把MAP2和REDUCE视为这个作业的核心,在他们之间使用标准的分区和洗牌操作,把MAP1视为预处理,把MAP3,MAP4视为后处理

示例代码如下:

public class ChainMaper {
public static void main(String[] args) throws IOException {
Configuration conf = new Configuration();

JobConf job = new JobConf(conf);

JobConf job1 = new JobConf(false);
JobConf job2 = new JobConf(false);
JobConf job3 = new JobConf(false);
JobConf job4 = new JobConf(false);
JobConf job5 = new JobConf(false);

ChainMapper.addMapper(job, (Class<? extends Mapper<LongWritable, Text, Text, Text>>) Map1.class, LongWritable.class,Text.class,Text.class, Text.class,true,job1);
ChainMapper.addMapper(job, (Class<? extends Mapper<Text, Text, LongWritable, Text>>) Map2.class, Text.class,Text.class, LongWritable.class,Text.class,true,job2);
ChainReducer.setReducer(job, (Class<? extends Reducer<LongWritable, Text, Text, Text>>) Reduce.class, LongWritable.class,Text.class,Text.class, Text.class,true,job3);
ChainMapper.addMapper(job, (Class<? extends Mapper<Text, Text, LongWritable, Text>>) Map4.class, Text.class,Text.class, LongWritable.class,Text.class,true,job4);
ChainMapper.addMapper(job, (Class<? extends Mapper<LongWritable, Text, LongWritable, Text>>) Map5.class, LongWritable.class,Text.class, LongWritable.class,Text.class,true,job5);

JobClient.runJob(job);
}
}

2、联结不同来源的数据

在数据分析中不可避免地需要从不同的来源提取数据。在Hadoop中数据的联结更为复杂,并有几种可能的方法,需做不同的权衡

假设有如下数据记录:(数据之间以,分隔)

客户ID 名字 电话
1 SS 555
2 EE 123
3 JJ 281
4 DD 408
客户ID 订单ID 价格 交易时间
3 A 12 02-Jun-2008
2 B 88 20-May-2008
3 C 32 30-Nov-2007
3 D 25 22-Jan-2009

预计的输出结果如下:

客户ID 名字 号码 订单ID 价钱 交易时间
1 SS 555 B 88 20-May-2008
2 EE 123 C 32 30-Nov-2007
3 JJ 281 A 12 02-Jun-2008
3 JJ 281 D 25 22-Jan-2009

2-1、Reduce侧的联结

1、Reduce侧联结的数据流

与数据库处理表的连接类似:

  • 需要用户自定义一个组键(Group Key)
  • 并且为每个记录定义一个标签(tag),表示其数据的来源,标签确保这个元信息一直跟随记录

处理步骤如下:

  1. 每个map处理一个文件,在示例中有2个文件,来自2个不同的数据源
  2. 将每个记录打包,使其可以在reduce侧联结
  3. map输出的记录中的键是用户自定义的组键(group key)
  4. 然后进行MR框架的洗牌和排序操作
  5. reduce会接收到相同的组键(group key)的记录,其中包括来自2个文件的数据记录,分别带有不同的标签(tag)
  6. 输出联结后的记录

Tips:存在这样的情况,reduce处理记录时group key1 对于tag1有一条记录,而tag2有2条记录时,那么得到的结果是他们的完全交叉乘积。即group key1 tag1 tag2-1 和group key1 tag1 tag2-2 这样的2条记录

2、使用DATAJOIN软件包实现联结
Hadoop有一个名为datajoun的软件包,用作数据联结的通用框架。有3个可供继承的抽象类:

  1. DataJoinMapperBase
  2. DataJoinReducerBase
  3. TaggedMapOutput:是一种用Text标签封装记录的数据类型,实现了getTag(),setTag(Text Tag)方法,抽象方法getData()【作为mapper的输出, TaggedMapOutput必须为Writable类型,因此,必须实现readFields()和write()方法】

Datajoin软件包已经在这些基类上实现了map()和reduce()方法,可用于执行联结数据流。

2-2、基于DistributedCache的复制联结

Hadoop有一种称为分布式缓存的机制,设计主旨在于将文件分布到集群中所有节点上(通常用于分发包含所有mapper所需“背景”数据文件)

使用这个类的步骤:

  1. 配置作业时,调用静态方法DistributedCache.addCacheFile()来设定要传播到所有节点上的文件(这些文件被指定为URI对象,除非设置了不同的文件系统,否则都保存在HDFS中)JobTracker在开始工作时,将取得URI列表,并在所有TaskTracker中创建文件的本地副本
  2. 在每个单独的TaskTracker上的mapper会调用静态方法DistributedCache.getLoalCacheFiles()来获取数组本地副本所在的本地文件的路径

2-3、半联结:map侧过滤后在reduce侧联结

使用复制连接的限制之一是其中一个连接表必须小到可以放在内存中,解决这个问题的方式:重新组织处理的步骤让处理变得更为有效。

reduce侧联结的主要问题是数据仅仅有mapper做了标签,然后全部在网络上重排,而它们大多数都被reducer所忽略。如果mapper在网络重排以前,用一个额外的预过滤函数去除大多数都不必要的数据,效率低下的问题会得到改善。

3、创建一个Bloom filter

3-1、Bloom filter简介

Bloom filter是一个数据集的摘要,它的使用让其他的数据处理技术更为有效。

Bloom filter对象支持2个方法:add()和contains()【存在小概率的误报】,这2个方法的运行方式与Java中Set接口类似。