附录D.2 复制连接框架

复制连接是map端连接,得名于它的具体实现:连接中最小的数据集将会被复制到所有的map主机节点。复制连接的实现非常直接明了。更具体的内容可以参考Chunk Lam的《Hadoop in Action》。

这个部分的目标是:创建一个可以支持任意类型的数据集的通用的复制连接框架。这个框架中提供了一个优化的小功能:动态监测分布式缓存内容和输入块的大小,并判断哪个更大。如果输入块较小,那么你就需要将map的输入块放到内存缓冲中,然后在map的cleanup方法中执行连接操作了。

图D.4是这个框架的类图,这里提供了连接类(GenericReplicatedJoin)的具体实现,而不仅仅是一个抽象类。在这个框架外,这个类将和KeyValueTextInputFormat及TextOutputFormat协作。它的一个假设前提是:每个数据文件的第一个标记是连接键。此外,连接类也可以被继承扩展来支持任意类型的输入和输出。

图D.5是连接框架的算法。Map的setup方法判断在map的输入块和分布式缓存中的内容哪个大。如果分布式缓存的内容比较小,那么它将被装载到内存缓存中。然后在Map函数开始连接操作。如果输入块比较小,map函数将输入块的键\值对装载到内存缓存中。Map的cleanup方法将从分布式缓存中读取记录,逐条记录和在内存缓存中的键\值对进行连接操作。

以下代码是GenericReplicatedJoin类中setup方法。它在map的初始化阶段被调用的。这个方法判断分布式缓存中的文件和输入块哪个大。如果文件比较小,则将文件装载到HashMap中。

 1 @Override
2 protected void setup(Context context)
3 throws IOException, InterruptedException {
4
5 distributedCacheFiles = DistributedCache.getLocalCacheFiles(context.getConfiguration());
6 int distCacheSizes = 0;
7
8 for (Path distFile : distributedCacheFiles) {
9 File distributedCacheFile = new File(distFile.toString());
10 distCacheSizes += distributedCacheFile.length();
11 }
12
13 if(context.getInputSplit() instanceof FileSplit) {
14 FileSplit split = (FileSplit) context.getInputSplit();
15 long inputSplitSize = split.getLength();
16 distributedCacheIsSmaller = (distCacheSizes < inputSplitSize);
17 } else {
18 distributedCacheIsSmaller = true;
19 }
20
21 if (distributedCacheIsSmaller) {
22 for (Path distFile : distributedCacheFiles) {
23 File distributedCacheFile = new File(distFile.toString());
24 DistributedCacheFileReader reader = getDistributedCacheReader();
25 reader.init(distributedCacheFile);
26
27 for (Pair p : (Iterable<Pair>) reader) {
28 addToCache(p);
29 }
30
31 reader.close();
32 }
33 }
34 }

根据setup方法是否将分布式缓存的内容装载到内存的缓存中,Map方法将会有不同的行为。如果分布式缓存中的内容被装载到内存中,那么map方法就将输入块的记录和内存中的缓存做连接操作。如果分布式缓存中的内容没有被装载到内存中,那么map方法就将输入块的记录装载到内存中,然后在cleanup方法中使用。

 1 @Override
2 protected void map(Object key, Object value, Context context)
3 throws IOException, InterruptedException {
4 Pair pair = readFromInputFormat(key, value);
5
6 if (distributedCacheIsSmaller) {
7 joinAndCollect(pair, context);
8 } else {
9 addToCache(pair);
10 }
11 }
12
13 public void joinAndCollect(Pair p, Context context)
14 throws IOException, InterruptedException {
15 List<Pair> cached = cachedRecords.get(p.getKey());
16
17 if (cached != null) {
18 for (Pair cp : cached) {
19 Pair result;
20
21 if (distributedCacheIsSmaller) {
22 result = join(p, cp);
23 } else {
24 result = join(cp, p);
25 }
26
27 if (result != null) {
28 context.write(result.getKey(), result.getData());
29 }
30 }
31 }
32 }
33
34 public Pair join(Pair inputSplitPair, Pair distCachePair) {
35 StringBuilder sb = new StringBuilder();
36
37 if (inputSplitPair.getData() != null) {
38 sb.append(inputSplitPair.getData());
39 }
40
41 sb.append("\t");
42
43 if (distCachePair.getData() != null) {
44 sb.append(distCachePair.getData());
45 }
46
47 return new Pair<Text, Text>(
48 new Text(inputSplitPair.getKey().toString()),
49 new Text(sb.toString()));
50 }

当所有的记录都被传输给map方法后,MapReduce将会调用cleanup方法。如果分布式缓存中的内容比输入块大,连接将会在cleanup中进行。连接的对象是map函数的缓存中的输入块的记录和分布式缓存中的记录。

 1 @Override
2 protected void cleanup(Context context)
3 throws IOException, InterruptedException {
4
5 if (!distributedCacheIsSmaller) {
6
7 for (Path distFile : distributedCacheFiles) {
8 File distributedCacheFile = new File(distFile.toString());
9 DistributedCacheFileReader reader = getDistributedCacheReader();
10 reader.init(distributedCacheFile);
11
12 for (Pair p : (Iterable<Pair>) reader) {
13 joinAndCollect(p, context);
14 }
15
16 reader.close();
17 }
18 }
19 }

最后,作业的驱动代码必须指定需要装载到分布式缓存中的文件。以下的代码可以处理一个文件,也可以处理MapReduce输入结果的一个目录。

 1 Configuration conf = new Configuration();
2
3 FileSystem fs = smallFilePath.getFileSystem(conf);
4 FileStatus smallFilePathStatus = fs.getFileStatus(smallFilePath);
5
6 if(smallFilePathStatus.isDir()) {
7 for(FileStatus f: fs.listStatus(smallFilePath)) {
8 if(f.getPath().getName().startsWith("part")) {
9 DistributedCache.addCacheFile(f.getPath().toUri(), conf);
10 }
11 }
12 } else {
13 DistributedCache.addCacheFile(smallFilePath.toUri(), conf);
14 }

这个框架假设分布式缓存中的内容和输入块的内容都可以被装载到内存中。它的优点在于两个数据集之中较小的才会装载到内存中。

在论文《A Comparison of Join Algorithms for Log Processing in MapReduce》中,针对对于分布式缓存中的内容较大时的场景对这个方法进行了更多的优化。在他们的优化中,他们将分布式缓存分成N个分区,并将输入块放入N个哈希表。然后在cleanup方法中的优化就更加高效。

在map端的复制连接的问题在于,map任务必须在启动时读取分布式缓存。上述论文提到的另一个优化方案是重载FileInputFormat的splitting。将存在于同一个主机上的输入块合并成一个块。然后就可以减少需要装载分布式缓存的map任务的个数了。

最后一个说明,Hadoop在org.apache.hadoop.mapred.join包中自带了map端的连接。但是它需要有序的待连接的数据集的输入文件,并要求将其分发到相同的分区中。这样就造成了繁重的预处理工作。

[大牛翻译系列]Hadoop(22)附录D.2 复制连接框架的更多相关文章

  1. [大牛翻译系列]Hadoop 翻译文章索引

    原书章节 原书章节题目 翻译文章序号 翻译文章题目 链接 4.1 Joining Hadoop(1) MapReduce 连接:重分区连接(Repartition join) http://www.c ...

  2. [大牛翻译系列]Hadoop(3)MapReduce 连接:半连接(Semi-join)

    4.1.3 半连接(Semi-join) 假设一个场景,需要连接两个很大的数据集,例如,用户日志和OLTP的用户数据.任何一个数据集都不是足够小到可以缓存在map作业的内存中.这样看来,似乎就不能使用 ...

  3. [大牛翻译系列]Hadoop(2)MapReduce 连接:复制连接(Replication join)

    4.1.2 复制连接(Replication join) 复制连接是map端的连接.复制连接得名于它的具体实现:连接中最小的数据集将会被复制到所有的map主机节点.复制连接有一个假设前提:在被连接的数 ...

  4. [大牛翻译系列]Hadoop(21)附录D.1 优化后的重分区框架

    附录D.1 优化后的重分区框架 Hadoop社区连接包需要将每个键的所有值都读取到内存中.如何才能在reduce端的连接减少内存开销呢?本文提供的优化中,只需要缓存较小的数据集,然后在连接中遍历较大数 ...

  5. [大牛翻译系列]Hadoop(1)MapReduce 连接:重分区连接(Repartition join)

    4.1 连接(Join) 连接是关系运算,可以用于合并关系(relation).对于数据库中的表连接操作,可能已经广为人知了.在MapReduce中,连接可以用于合并两个或多个数据集.例如,用户基本信 ...

  6. [大牛翻译系列]Hadoop(20)附录A.10 压缩格式LZOP编译安装配置

    附录A.10 LZOP LZOP是一种压缩解码器,在MapReduce中可以支持可分块的压缩.第5章中有一节介绍了如何应用LZOP.在这一节中,将介绍如何编译LZOP,在集群做相应配置. A.10.1 ...

  7. [大牛翻译系列]Hadoop(19)MapReduce 文件处理:基于压缩的高效存储(二)

    5.2 基于压缩的高效存储(续) (仅包括技术27) 技术27 在MapReduce,Hive和Pig中使用可分块的LZOP 如果一个文本文件即使经过压缩后仍然比HDFS的块的大小要大,就需要考虑选择 ...

  8. [大牛翻译系列]Hadoop(18)MapReduce 文件处理:基于压缩的高效存储(一)

    5.2 基于压缩的高效存储 (仅包括技术25,和技术26) 数据压缩可以减小数据的大小,节约空间,提高数据传输的效率.在处理文件中,压缩很重要.在处理Hadoop的文件时,更是如此.为了让Hadoop ...

  9. [大牛翻译系列]Hadoop(17)MapReduce 文件处理:小文件

    5.1 小文件 大数据这个概念似乎意味着处理GB级乃至更大的文件.实际上大数据可以是大量的小文件.比如说,日志文件通常增长到MB级时就会存档.这一节中将介绍在HDFS中有效地处理小文件的技术. 技术2 ...

随机推荐

  1. MyBatis 环境搭建

    1.为什么我们学习框架? 提高开发效率,框架是别人写好的工具类,我们需要遵循其规则进行操作 2.我们学习哪些框架 A.持久层框架:MyBatis 什么是持久化? 狭义:把数据永久性的保存到数据当中 广 ...

  2. c++的输入流基础知识

    cin是istream类的对象,它从标准输入设备获取数据,程序中的变量通过流提取符“>>”从流中提取数据.从流中提取数据时通常跳过输入流中的空白符   只有在输入完数据并按回车后,该行数据 ...

  3. 重构8-Replace Inheritance with Delegation(委托替换继承)

    继承的误用十分普遍.它只能用于逻辑环境,但却经常用于简化,这导致复杂的没有意义的继承层次.看下面的代码: public class Sanitation{ public String WashHand ...

  4. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '

    mysql中如果字段使用了关键字,在插入和更新时会提示 You have an error in your SQL syntax; check the manual that corresponds ...

  5. cannot find module 'cordova-common'

    解决办法,重新安装cordova npm install -g cordova

  6. 如何使用OpenShare部署和运营企业门户

    如何使用OpenShare部署和运营企业门户 这篇Blog是偏向企业内整体门户部署和运营的指南,是偏向整体管理和规划的,并不是针对终端用户的OpenShare软件操作手册,具体的操作可以上优酷看相关视 ...

  7. 【php基础】XML,DTD实例

    要求一:能看懂别人写的DTD文件,自己能写更好.例如products.dtd: <!ENTITY AUTHOR "John Doe"> <!ENTITY COMP ...

  8. Android处理Bitmap使其能够不失真等比缩放裁剪后显示在ImageView上

    Android开发过程中,我们有时需要动态得显示一些图片,并且这些图片的大小差距会十分大,如果需求并不是需要图片完整显示,但是需要不失真,并且要图片中间部分的情况下,我们需要做一系列处理,因为这个时候 ...

  9. JavaScript之六种排序法

    1.冒泡排序循环的最大值从length递减每次循环只能排好最后一个,然后递减到第一个 function bubbleSort(){ var changedData = new Array(); var ...

  10. 2015英特尔® 实感™ (Intel® RealSense™) 动手开发实验课

    2015年英特尔® 全球实感技术动手实验课路演来到中国, 这次在中国将有北京和广州两站,包括一天的动手实验室活动 - 面向对感知计算.3D 开发和虚拟现实兴趣浓厚的开发人员.英特尔专家将会指导您如何借 ...