Hadoop流 - 从reducer输出中删除尾随选项卡

时间:2013-08-08 18:22:57

标签: hadoop hadoop-streaming

我有一个hadoop流作业,其输出不包含键/值对。您可以将其视为仅限值对或仅键对。

我的流式缩减器(php脚本)输出由换行符分隔的记录。 Hadoop流将其视为没有值的密钥,并在换行符之前插入一个选项卡。这个额外的标签是不需要的。

如何删除它?

我正在使用带有AWS EMR的hadoop 1.0.3。我下载了hadoop 1.0.3的源代码,并在hadoop-1.0.3 / src / contrib / streaming / src / java / org / apache / hadoop / streaming / PipeReducer.java中找到了这段代码:

reduceOutFieldSeparator = job_.get("stream.reduce.output.field.separator", "\t").getBytes("UTF-8");

所以我尝试将-D stream.reduce.output.field.separator=作为参数传递给工作而没有运气。我还试过-D mapred.textoutputformat.separator=-D mapreduce.output.textoutputformat.separator=但没有运气。

我当然搜索过google,而我找不到任何工作。一个搜索结果甚至表明,没有任何论据可以通过以达到预期的结果(但是,在这种情况下,hadoop版本真的很旧)。

这是我的代码(为了便于阅读,添加了换行符):

hadoop jar streaming.jar -files s3n://path/to/a/file.json#file.json
    -D mapred.output.compress=true -D stream.reduce.output.field.separator=
    -input s3n://path/to/some/input/*/* -output hdfs:///path/to/output/dir
    -mapper 'php my_mapper.php' -reducer 'php my_reducer.php'

3 个答案:

答案 0 :(得分:10)

对其他人有帮助,使用上面的提示,我能够实现:

CustomOutputFormat<K, V> extends org.apache.hadoop.mapred.TextOutputFormat<K, V> {....}

将'getRecordWriter'的内置实现中只有一行更改为:

String keyValueSeparator = job.get("mapred.textoutputformat.separator", ""); 

而不是:

String keyValueSeparator = job.get("mapred.textoutputformat.separator", "\t"); 

将其编译成Jar后,将其包含在我的hadoop流式呼叫中(通过hadoop流式传输的说明),调用如下:

hadoop   jar  /usr/lib/hadoop/contrib/streaming/hadoop-streaming-1.0.3.jar     \
-archives 'hdfs:///user/the/path/to/your/jar/onHDFS/theNameOfTheJar.jar' \
-libjars theNameOfTheJar.jar \
-outputformat com.yourcompanyHere.package.path.tojavafile.CustomOutputFormat  \
-file yourMapper.py    -mapper  yourMapper.py     \
-file yourReducer.py   -reducer yourReducer.py    \
-input $yourInputFile    \
-output $yourOutputDirectoryOnHDFS

我还将jar包含在我发出的调用文件夹中。

这对我的需求非常有效(并且它在减速器之后的行尾没有制作标签)。


更新:根据评论暗示这确实对其他人有帮助,这里是我的CustomOutputFormat.java文件的完整来源:

import java.io.DataOutputStream;
import java.io.IOException;

import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.io.compress.GzipCodec;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.RecordWriter;
import org.apache.hadoop.mapred.TextOutputFormat;
import org.apache.hadoop.util.Progressable;
import org.apache.hadoop.util.ReflectionUtils;

public class CustomOutputFormat<K, V> extends TextOutputFormat<K, V> {

    public RecordWriter<K, V> getRecordWriter(FileSystem ignored, JobConf job, String name,
        Progressable progress) throws IOException {
    boolean isCompressed = getCompressOutput(job);

    //Channging the default from '\t' to blank
    String keyValueSeparator = job.get("mapred.textoutputformat.separator", ""); // '\t'
    if (!isCompressed) {
        Path file = FileOutputFormat.getTaskOutputPath(job, name);
        FileSystem fs = file.getFileSystem(job);
        FSDataOutputStream fileOut = fs.create(file, progress);
        return new LineRecordWriter<K, V>(fileOut, keyValueSeparator);
    } else {
        Class<? extends CompressionCodec> codecClass = getOutputCompressorClass(job,
            GzipCodec.class);
        // create the named codec
        CompressionCodec codec = ReflectionUtils.newInstance(codecClass, job);
        // build the filename including the extension
        Path file = FileOutputFormat.getTaskOutputPath(job, name + codec.getDefaultExtension());
        FileSystem fs = file.getFileSystem(job);
        FSDataOutputStream fileOut = fs.create(file, progress);
        return new LineRecordWriter<K, V>(new DataOutputStream(
            codec.createOutputStream(fileOut)), keyValueSeparator);
    }
    }
}

仅供参考:对于您的使用情况,请务必检查这不会对您的映射器和缩减器之间的hadoop-streaming管理交互(在分离键与值之间)产生负面影响。澄清:

  • 从我的测试中 - 如果你的数据的每一行都有一个“标签”(每侧有一些东西),你可以保留内置的默认值:流将解释第一个在第一个标签之前作为您的“关键字”的所有内容,以及作为您的“价值”之后的所有内容。因此,它没有看到“空值”,并且不会附加在缩减器后显示的选项卡。 (你会看到你的最终输出按照'关键'的值排序,它将每行中的流解释为在每个标签之前发生的结果。

  • 相反,如果您的数据中没有标签,并且您没有使用上述技巧覆盖默认值,那么您将在运行完成后看到标签,上面的覆盖成为一个修复。

答案 1 :(得分:7)

查看org.apache.hadoop.mapreduce.lib.output.TextOutputFormat源代码,我看到了两件事:

  1. 如果key或value为非null,则write(key,value)方法会写入分隔符
  2. \t返回null(我假设发生在mapred.textoutputformat.separator
  3. 时,使用默认值(-D stream.reduce.output.field.separator=)始终设置分隔符

    您唯一的解决方案可能是编写自己的OutputFormat,以解决这两个问题。

    我的测试

    在我的任务中,我想从

    重新格式化一行
    id1|val1|val2|val3
    id1|val1
    

    成:

    id1|val1,val2,val3
    id2|val1
    

    我有一个自定义映射器(Perl脚本)来转换行。对于此任务,我最初尝试将其作为仅键输入(或仅限值)输入,但使用尾随选项卡获得结果。

    起初我刚刚指定:

      

    -D stream.map.input.field.separator ='|' -D stream.map.output.field.separator ='|'

    这给了映射器一个键值对,因为我的映射无论如何都需要一个键。但是这个输出现在在第一个字段之后有了标签

    当我添加时,我得到了所需的输出:

      

    -D mapred.textoutputformat.separator ='|'

    如果我没有设置它或设置为空白

      

    -D mapred.textoutputformat.separator =

    然后我会在第一个字段后再次获得一个标签。

    一看到TextOutputFormat

    的源代码就有意义了

答案 2 :(得分:2)

我也有这个问题。我正在使用python,仅限地图的作业,基本上只是发送CSV数据行。检查输出后,我注意到每行末尾的\ t。

 foo,bar,baz\t

我发现映射器和Python流都处理键值对。如果您不发出默认分隔符,则整行CSV数据被视为“键”,而框架(需要键和值)会打开\ t和空值。

由于我的数据本质上是一个CSV字符串,因此我将stream和mapred输出的分隔符​​设置为逗号。框架将第一个逗号的所有内容读取为键,将第一个逗号后的所有内容读作值。然后,当它将结果写到文件时,它写了 键逗号值 ,这有效地创建了我之后的输出。

 foo,bar,baz

在我的情况下,我添加了以下内容以防止框架将\ t添加到我的csv输出的末尾...

-D mapred.reduce.tasks=0 \
-D stream.map.output.field.separator=, \
-D mapred.textoutputformat.separator=, \