我有三个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来解决这个问题?
答案 0 :(得分:1)
可以对发射值进行简单标记,因为只涉及三种类型的文件。在地图中提取拆分的路径,标识其位置并为该值添加合适的前缀。为清楚起见,请说输出在3个目录中:
将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
对象创建开销。