如果使用MapReduce执行的操作不是可交换和关联的,则组合器不能与reducer相同。
例如,在计算平均值时,组合器将键和减值器总和的值相加,然后将总和除以该键的值总数。组合器的代码只有一点修改。如果您可以对组合器和减速器使用相同的类并且具有可以确定当前任务是组合器还是减速器的代码安静怎么办?如果它发现它是一个reducer而不是它除以总数。
这样的事情:
protected void reduce(Text keyIn, Iterable<PairWritable> valuesIn,
Context context)
throws IOException, InterruptedException {
double sum = 0.0d;
long count = 0l;
for (PairWritable valueIn : valuesIn) {
sum += valueIn.getSum();
count += valueIn.getCount();
}
if (THIS_IS_A_REDUCER) {
sum /= count;
}
context.write(keyIn, new PairWritable(sum, count));
}
有可能这样做吗?上面的代码THIS_IS_A_REDUCER
的和平可以被某些东西取代吗?
我可以确定任务是来自任务尝试ID字符串的映射器还是还原器,但是两个组合器和缩减器似乎都有类似的字符串模式。
答案 0 :(得分:3)
这是一个有缺陷的问题。每当您发现需要区分任务调用的reduce()时。添加一个组合器。例如,你写
public static class Combine extends MapReduceBase implements Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterator<Text> message, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {}
public static class Reduce extends MapReduceBase implements Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterator<Text> message, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {}
在main()中,你写了
conf.setReducerClass(Reduce.class);
conf.setCombinerClass(Combine.class);
答案 1 :(得分:3)
虽然我知道这个问题已经解决,但我有另一个解决方案。我所做的是让我的Combiner成为Reducer的子类。然后在Reducer代码中,我能够测试我是否是Combiner子类。
这样做的主要好处是我需要在Reducer步骤中修改我的键,但不想在Combining步骤中更改它(否则我会应用相同的转换两次)。除此之外,95%的代码是相同的。
答案 2 :(得分:2)
我想您可以询问Context
对象并获取任务ID。然后,一旦你有了ID,映射器(包括组合器)的名称就会有一个“m”,而reducer的名字中会有一个“r”。
要获取任务尝试ID,请使用.getTaskAttemptID()。我认为你应该能够context.getTaskAttemptID()
使用它,但我无法测试它。