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

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

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

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

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

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

  1. 1 Configuration conf = new Configuration();
  2. 2
  3. 3 FileSystem fs = smallFilePath.getFileSystem(conf);
  4. 4 FileStatus smallFilePathStatus = fs.getFileStatus(smallFilePath);
  5. 5
  6. 6 if(smallFilePathStatus.isDir()) {
  7. 7 for(FileStatus f: fs.listStatus(smallFilePath)) {
  8. 8 if(f.getPath().getName().startsWith("part")) {
  9. 9 DistributedCache.addCacheFile(f.getPath().toUri(), conf);
  10. 10 }
  11. 11 }
  12. 12 } else {
  13. 13 DistributedCache.addCacheFile(smallFilePath.toUri(), conf);
  14. 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. 查找字符对应Unicode码的十进制数字

    //将字符转换为Unicode码中字符对应十进制数字 int byte0 = 'A' & 0xff;//byte0=65 参考文档:http://baike.baidu.com/view/26 ...

  2. [Java,JavaEE] 最常用的Java库一览

    引用自:http://www.importnew.com/7530.html 本文由 ImportNew - 邢 敏 翻译自 programcreek.欢迎加入Java小组.转载请参见文章末尾的要求. ...

  3. 【Shell脚本学习15】shell printf命令:格式化输出语句

    printf 命令用于格式化输出, 是echo命令的增强版.它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同. 注意:printf 由 POSIX 标准所定义,移植性要比 ech ...

  4. Spring学习总结一——SpringIOC容器一

    什么是spring spring是一个开源的轻量级的应用开发框架,它提供了IOC和AOP应用,可以减少组件之间的耦合度,即 解耦,spring容器可以创建对象并且管理对象之间的关系. 一:实例化spr ...

  5. 20145102 《Java程序设计》第2周学习总结

    20145102 <Java程序设计>第2周学习总结 教材学习内容总结 这章先介绍了基本类型,和其他语言相似,分为short整数(2字节).int整数(4字节).long整数(8字节).f ...

  6. 简单介绍AngularJs Filters

    网站链接:http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/angular-filters/ Filter作用就是接收一个输入,通过某 ...

  7. 设置背景为白色,避免从A视图跳转到B视图的时候出现卡顿

    - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; }

  8. oracle 数据导入导出命令

    1.数据导出:  1 将数据库TEST完全导出,用户名system 密码manager 导出到D:\daochu.dmp中   exp system/manager@TEST file=d:\daoc ...

  9. Bootstrap 基本用法

    使用bootstrap框架的步骤: 1.引用bootstrap的css框架,这样可以通过bootstrap来布局: <link rel="stylesheet" href=& ...

  10. Oracle 学习笔记3:新建数据库没有scott用户解决办法

    新建一个数据库,若选择Oracle组件时,没有选择实例方案,完成后进行口令管理,默认列表中是找不到scott用户解锁的.若要解锁scott用户,可以进行如下操作: 使用system或者sys连接数据库 ...