Hadoop reducer字符串操作不起作用

时间:2010-10-01 19:34:36

标签: hadoop

您好 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上测试了这个 如果你能发现问题,让我知道它是什么......这真的会让我头上留下一些头发!

2 个答案:

答案 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);

解决了这个问题。