[英]Combining results from hadoop map-reduce
我有一个Mapper<AvroKey<Email>, NullWritable, Text, Text>
,它可以有效地接收电子邮件,并且多次吐出电子邮件地址的键和它在其上找到的字段的值(从,到,抄送,等等)。
然后,我有一个Reducer<Text, Text, NullWritable, Text>
,其中包含电子邮件地址和字段名称。 它吐出一个NullWritable密钥,并计算该地址在给定字段中存在多少次。 例如..
{
"address": "joe.bloggs@gmail.com",
"toCount": 12,
"fromCount": 4
}
我正在使用FileUtil.copyMerge合并作业的输出,但是(显然)不同的reducer的结果没有合并,因此在实践中我看到:
{
"address": "joe.bloggs@gmail.com",
"toCount": 12,
"fromCount": 0
}, {
"address": "joe.bloggs@gmail.com",
"toCount": 0,
"fromCount": 4
}
有没有更明智的方法来解决此问题,以便每个电子邮件地址都能得到一个结果? (我收集了一个运行预缩减阶段的合并器,它仅在数据的子集上运行,并且不能保证给出我想要的结果)?
编辑:
Reducer代码如下所示:
public class EmailReducer extends Reducer<Text, Text, NullWritable, Text> {
private static final ObjectMapper mapper = new ObjectMapper();
public void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
Map<String, Map<String, Object>> results = new HashMap<>();
for (Text value : values) {
if (!results.containsKey(value.toString())) {
Map<String, Object> result = new HashMap<>();
result.put("address", key.toString());
result.put("to", 0);
result.put("from", 0);
results.put(value.toString(), result);
}
Map<String, Object> result = results.get(value.toString());
switch (value.toString()) {
case "TO":
result.put("to", ((int) result.get("to")) + 1);
break;
case "FROM":
result.put("from", ((int) result.get("from")) + 1);
break;
}
results.values().forEach(result -> {
context.write(NullWritable.get(), new Text(mapper.writeValueAsString(result)));
});
}
}
reducer的每个输入键都对应一个唯一的电子邮件地址,因此您不需要results
收集。 每次调用reduce
方法时,它都是针对不同的电子邮件地址的,所以我的建议是:
public class EmailReducer extends Reducer<Text, Text, NullWritable, Text> {
private static final ObjectMapper mapper = new ObjectMapper();
public void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
Map<String, Object> result = new HashMap<>();
result.put("address", key.toString());
result.put("to", 0);
result.put("from", 0);
for (Text value : values) {
switch (value.toString()) {
case "TO":
result.put("to", ((int) result.get("to")) + 1);
break;
case "FROM":
result.put("from", ((int) result.get("from")) + 1);
break;
}
context.write(NullWritable.get(), new Text(mapper.writeValueAsString(result)));
}
}
我不确定ObjectMapper类的作用,但是我想您需要它来格式化输出。 否则,我将输入键作为输出键(即电子邮件地址)打印出来,并将每个电子邮件地址的“发件人”和“收件人”字段的两个串联计数打印出来。
如果您的输入是数据收集(即,不是流或类似的东西),则每个电子邮件地址应仅获得一次。 如果您的输入是在流中给出的,并且您需要逐步构建最终输出,则一个作业的输出可以是另一个作业的输入。 如果是这种情况,我建议使用MultipleInputs(其中一个Mapper是您先前描述的Mapper,另一个IdentityMapper)将前一个作业的输出转发给Reducer。 同样,同一电子邮件地址由相同的reduce任务处理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.