您好 Reduce阶段的文本操作似乎无法正常工作。 我怀疑问题可能在我的代码中,而不是hadoop本身,但你永远不知道...... 如果你能发现任何陷阱让我知道。 我浪费了一天时间试图找出这段代码的错误。
我的示例输入文件名为simple.psv
12345 abc@bbc.com|m|1975
12346 bbc@cde.com|m|1981
我的Mapper和reducer代码
package simplemapreduce;
import java.io.IOException;
import java.util.Iterator;
import java.util.StringTokenizer;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.lib.KeyFieldBasedComparator;
/**
*
* @author
*/
public class Main {
public static class SimpleMap extends MapReduceBase implements
Mapper<LongWritable, Text, Text, Text> {
public void map(LongWritable key, Text inputs,
OutputCollector<Text, Text> output, Reporter reporter)
throws IOException {
String inputString = inputs.toString();
//System.out.println("CRM Map record:"+inputString);
StringTokenizer tokenizer = new StringTokenizer(inputString);
Text userid = new Text();
if (tokenizer.hasMoreTokens()) {
userid.set(tokenizer.nextToken());
Text data = new Text();
if (tokenizer.hasMoreTokens()) {
data.set(tokenizer.nextToken());
} else {
data.set("");
}
output.collect(userid, data);
}
}
}
/**
* A reducer class that just emits its input.
*/
public static class SimpleReduce extends MapReduceBase implements
Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterator<Text> values,
OutputCollector<Text, Text> output, Reporter reporter)
throws IOException {
while (values.hasNext()) {
Text txt = values.next();
String inputString = "<start>" + txt.toString() + "<end>";
Text out = new Text();
out.set(inputString);
//System.out.println(inputString);
output.collect(key, out);
}
}
}
public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.err.println("Usage: SimpleMapReduce <input path> <output path>");
System.exit(1);
}
JobConf conf = new JobConf(Main.class);
conf.setJobName("Simple Map reducer");
FileInputFormat.setInputPaths(conf, new Path(args[0]));
FileOutputFormat.setOutputPath(conf, new Path(args[1]));
conf.setMapperClass(SimpleMap.class);
conf.setCombinerClass(SimpleReduce.class);
conf.setReducerClass(SimpleReduce.class);
conf.setOutputKeyClass(Text.class);
conf.setOutputValueClass(Text.class);
conf.setNumReduceTasks(1);
JobClient.runJob(conf);
}
}
我的示例启动脚本名为simple.sh
#!/bin/bash
hadoop jar SimpleMapReduce.jar \
/full/path/to/input/simple.tsv /user/joebloggs/output
预期产出
12345 <start>abc@bbc.com|m|1975<end>
12346 <start>bbc@cde.com|m|1981<end>
实际输出
12345 <start><start>abc@bbc.com|m|1975<end><end>
12346 <start><start>bbc@cde.com|m|1981<end><end>
我在亚马逊s3以及Linux上测试了这个 如果你能发现问题,让我知道它是什么......这真的会让我头上留下一些头发!
答案 0 :(得分:4)
通过系统的基本数据流是:
Input -> Map -> Reduce -> output.
作为性能优化,已添加组合器以允许计算机(hadoop集群中的众多计算机之一)在将数据传输到运行实际减速器的系统之前对数据进行部分聚合。
在单词计数示例中,可以从这些值开始:
1 1 1 1 1 1 1 1 1 1
将它们组合成
3 4 2 1
并将它们缩减为最终结果
10
因此,组合器本质上是一种性能优化。 如果您没有指定组合器,它将不会更改通过的信息(即它是“身份缩减器”)。 因此,如果数据集保持有效,则只能将SAME类用作组合器和减速器。在你的情况下:这不是真的 - &gt;您的数据现在无效。
你这样做:
conf.setCombinerClass(SimpleReduce.class);
conf.setReducerClass(SimpleReduce.class);
因此,这会使您的mapper的输出通过您的reducer两次。 第一个补充说:“开始”&amp; “结束” 第二个添加“开始”&amp;再次“结束”。
简单的解决方案:
// conf.setCombinerClass(SimpleReduce.class);
conf.setReducerClass(SimpleReduce.class);
HTH
答案 1 :(得分:2)
我遇到了一个问题,其中reducer不会获得mapper发送的所有数据。 reducer只会到达output.collect将发出的特定部分。对于Eg。 输入数据:
12345 abc@bbc.com|m | 1975
12346 bbc@cde.com | m |1981
如果我说
output.collect(键,mail_id);
然后它不会得到接下来的两个字段 - 性别和出生年份。
// conf.setCombinerClass(SimpleReduce.class);
解决了这个问题。