如果使用MapReduce执行的操作不是交换和关联的,那么组合器不能与化简器相同。
例如,在计算平均值时,合并器对键的值求和,然后化简器求和,然后将总和除以该键的值总数。组合器的代码只有轻微的修改。如果可以对组合器和化简器使用相同的类,并且代码可以确定当前任务是组合器还是化简器,该怎么办?如果它发现它是一个化简器,则它将总和除以计数。
像这样:
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 字符串确定任务是映射器还是化简器,但合并器和化简器似乎都有相似的字符串模式。
这是一个有缺陷的问题。每当您发现需要区分任务调用哪个 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);
虽然我知道这个问题已经解决了,但我有另一个解决方案。我所做的是让我的合路器成为化简器的子类。然后在Reducer代码中,我能够测试我是否是Combiner子类。
这样做的主要好处是我需要在 Reducer 步骤中修改我的密钥,但不想在合并步骤中更改它(否则我会应用两次相同的转换)。除此之外,95%的代码是相同的。
你可以询问Context
对象并获取任务ID。然后,一旦你有了ID,映射器(包括组合器)的名称中将有一个"m",而化简器的名称中将有一个"r"。
若要获取任务尝试 ID,请使用 .getTaskTryID()。我认为你应该能够做context.getTaskAttemptID()
使用它,但我无法确定它。