附录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. C#操作串口总结

    Technorati 标签: C#,SerialPort,ReadTo,ReadTimeout        最近几天一直在调一个要长时间连续不断的操作串口,并且是多线程运行,不允许中断的服务.后来服 ...

  2. ScrollView嵌套ListView的滑动冲突问题,是看大神的方法的,作为学习以后用的到

    在工作中,曾多次碰到ScrollView嵌套ListView的问题,网上的解决方法有很多种,但是杂而不全.我试过很多种方法,它们各有利弊. 在这里我将会从使用ScrollView嵌套ListView结 ...

  3. ios 把毫秒值转换成日期 NSDate

    ios 把毫秒值转换成日期 (比较好用) 1343359790000 这是毫秒值------最佳解决方案-------------------- long long time=134335979000 ...

  4. Android 高级UI设计笔记16:ViewStub的应用

    1. ViewStub 在开发应用程序的时候,经常会遇到这样的情况,会在运行时动态根据条件来决定显示哪个View或某个布局. 那么最通常的想法就是把可能用到的View都写在上面,先把它们的可见性都设为 ...

  5. Android进阶笔记18:选用合适的IPC方式

    1. 相信大家都知道Android进程间通信方式很多,比如AIDL.Messenger等等,接下来我就总结一下这些IPC方式优缺点. 2. IPC方式的优缺点和适用场景 3. 附加:使用Intent实 ...

  6. 【阿里云产品公测】OTS使用之简单线上产品实践基于PythonSDK

    阿里云用户:morenocjm 实践是检验真理的唯一标准,学习技术需要通过实践过程中的不断尝试,才能够快速掌握要领.OTS是构建在阿里云飞天分布式系统之上的NoSQL数据库服务,提供海量结构化数据的存 ...

  7. 【Mood 16 】史上最全github使用方法:github入门到精通

    [初识Github] 首先让我们大家一起喊一句“Hello Github”.YEAH!就是这样. Git 是一个分布式的版本控制系统,最初由Linus Torvalds编写,用作Linux内核代码的管 ...

  8. 【Linux/Ubuntu学习5】Ubuntu 下android 开发,eclipse不能识别手机

    ubuntu下eclipse不能识别手机解决方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1) 在终端运行 lsusb 会发现结果 ...

  9. 使用 xcode 8 构建版本 iTunes Connect 获取不到应用程序的状态

    今天在提交 APP 审核版本的时候iTunes Connect 一直获取不到应用程序的状态,原因是,14号 xcode 8一出 直接升级使用 xcode 8 打包发布包,然后直接用 xcode8 构建 ...

  10. 放飞App:移动产品经理实战指南

    <放飞App:移动产品经理实战指南> 基本信息 原书名:App savvy:rurning ideas into iPhone and iPad Apps customers really ...