按键合并制表符分隔文件

时间:2014-10-27 12:49:40

标签: java hadoop mapreduce

我有三个MapReduce作业,它们生成制表符分隔文件,这些文件在相同的文件上运行。第一个值是关键。这三个MR作业的每个输出都是这种情况。

我现在要做的是使用MapReduce来实现#34;缝合"这些文件一起按键。什么是最好的Mapper输出和Reducer输入?我尝试使用ArrayWritable,但由于shuffle,对于某些记录,1文件中的ArrayWritable位于第三位,而不是第二位。

我想要这个:

Key \t Values-from-first-MR-job \t Values-from-second-MR-job \t Values-from-third-MR-job

对于所有记录,这应该是相同的。但是,正如我所说,由于洗牌,有时会出现一些记录:

Key \t Values-from-third-MR-job \t Values-from-first-MR-job \t Values-from-second-MR-job

我应该如何设置Mapper和Reducer来解决这个问题?

1 个答案:

答案 0 :(得分:1)

可以对发射值进行简单标记,因为只涉及三种类型的文件。在地图中提取拆分的路径,标识其位置并为该值添加合适的前缀。为清楚起见,请说输出在3个目录中:

  1. PATH1 / mr_out_1
  2. PATH2 / mr_out_2
  3. path3时/ mr_out_3
  4. TextInputForamt用于所有这些路径,在map中您将执行以下操作:

    String[] keyVal = value.spilt("\t",2);
    
    Path filePath = ((FileSplit) context.getInputSplit()).getPath();
    String dirName = filePath.getParent().getName().toString();
    
    Text outValue = new Text();
    if(dirName.equals("mr_out_1")){
        outValue.set("1_" + keyVal[1]);
    } else if(dirName.equals("mr_out_2")){
        outValue.set("2_" + keyVal[1]);
    } else {
        outValue.set("3_" + keyVal[1]);
    }
    
    context.write(new Text(keyVal[0]), outVal);
    

    如果所有文件都在同一目录中,请使用fileName而不是dirName。然后根据名称识别标志(正则表达式匹配可能是合适的):

    String fileName = filePath.getName().toString();
    if(fileName.matches("regex")){ ... } 
    

    reduce中,只需将传入值放入List并进行排序。休息很简单。

    List<String> list = new ArrayList<String>(3);
    for(Text v : values){
        list.add(v.toString());     
    }
    Collections.sort(list);
    
    StringBuilder builder = new StringBuilder();
    for(String s : list){
        builder.append(s.substring(2)+"\t");    
    } 
    
    context.write(key, new Text(builder.toString().trim()));
    

    我认为它将达到目的。请记住,如果有超过9个文件(由于字母顺序),Collection.sort策略将失败。然后,您可以单独提取标记,将其转换为Integer并使用TreeMap<tag, actualString>进行排序。

    注意:以上所有代码段都使用的是新API。我没有使用IDE来编写这些,因此可能存在很少的语法错误。我再次没有遵循适当的书面惯例。假设map的outKey可以是类成员,使用outKey.set(keyVal[0])可以删除Text对象创建开销。