附录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. A4 打印长宽

    我是用下面的转换公式,将A4纸转为在屏幕中的分辨率 页边距: 0.75 inchA4: 8.27x11.69 inch打印机DPI:600DPI屏幕DPI  : 96DPIwidth = (8.27  ...

  2. Design Mode 之 行为模式

    行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 看看这11中模式的关系,大致可分为四类:(1) ...

  3. Precompile Prefix file(.pch文件)

    参考资料: http://blog.csdn.net/lwjok2007/article/details/46385595 http://www.tuicool.com/articles/beURbe ...

  4. (一)u-boot2013.01.01 for TQ210:《Uboot简介》

    一直想写一个s5pv210硬件平台的u-boot的移植文档,但一直都忙着没时间写.先写一些u-boot的脚本分析吧,包括makefile,mkconfig,config.mk,主要侧重于语法句意的分析 ...

  5. Centos7 安装redis3.2.3 过程

    1:安装wget: yum install wget2:安装pip:    1:sudo yum -y install epel-release    2:sudo yum -y install py ...

  6. php用curl获取远端网页内容

    <?php $url="http://www.baidu.com";$cc=curl_init(); curl_setopt($cc,CURLOPT_URL,$url); c ...

  7. poj 3034 动态规划

    思路:这是一道坑爹的动态规划,思路很容易想到,就是细节. 用dp[t][i][j],表示在第t时间,锤子停在(i,j)位置能获得的最大数量.那么只要找到一个点转移到(i,j)收益最大即可. #incl ...

  8. 使用开源库MagicalRecord操作CoreData

      1. 将 MagicalRecord 文件夹拖入到工程文件中,引入 CoreData.frame 框架 2. 在 .pch 文件中引入头文件 CoreData+MagicalRecord.h 注: ...

  9. WPF 多语言 多资源 多皮肤 处理方案

    同时兼容这么多需求的解决方案 我想到的 只有通过 动态切换加载资源字典  前端用绑定的模式 达到托管最大化 多语言举例 我编辑了 两个 语言包 一个中文 一个英文  (语言包这个最好用T4 写个模板, ...

  10. VS预生成事件命令行 和 生成后事件命令行

    宏                                                      说明 $(ConfigurationName)            当前项目配置的名称( ...