将snappy压缩文件加载到Elastic MapReduce中

时间:2013-03-21 21:17:39

标签: hadoop amazon-web-services compression hadoop-streaming emr

我在S3中有一堆snappy压缩的服务器日志,我需要在Elastic MapReduce上使用流处理它们。我如何告诉亚马逊和Hadoop日志已经被压缩(在它们被拉入HFS之前!),以便它们可以在被发送到流式映射器脚本之前被解压缩?

我能找到的唯一文档是:http://docs.aws.amazon.com/ElasticMapReduce/latest/DeveloperGuide/HadoopDataCompression.html#emr-using-snappy ,它似乎是指中间压缩,而不是指到达HFS时压缩的文件。

顺便说一下,我主要是在python工作,所以如果你有一个boto的解决方案,那么奖励积分!

2 个答案:

答案 0 :(得分:7)

答案是,“它无法完成。”至少,不是因为将hadoop流应用于源自hadoop之外的snappy压缩文件的特定情况。

我(彻底!)探索了两个主要选项来得出这个结论:(1)尝试使用hadoop内置的snappy压缩,如高度咖啡因所暗示的,或者(2)编写我自己的流模块来使用和解压缩snappy文件

对于选项(1),似乎hadoop在使用snappy压缩文件时为文件添加了一些标记。由于我的文件是使用snappy在hadoop外部压缩的,因此hadoop的内置编解码器无法解压缩文件。

此问题的一个症状是堆空间错误:

2013-04-03 20:14:49,739 FATAL org.apache.hadoop.mapred.Child (main): Error running child : java.lang.OutOfMemoryError: Java heap space
    at org.apache.hadoop.io.compress.BlockDecompressorStream.getCompressedData(BlockDecompressorStream.java:102)
    at org.apache.hadoop.io.compress.BlockDecompressorStream.decompress(BlockDecompressorStream.java:82)
    at org.apache.hadoop.io.compress.DecompressorStream.read(DecompressorStream.java:76)
    at java.io.InputStream.read(InputStream.java:85)
    ...

当我切换到一个更大的实例并启动了mapred.child.java.opts设置时,我遇到了一个新错误:

java.io.IOException: IO error in map input file s3n://my-bucket/my-file.snappy

Hadoop的snappy编解码器无法使用外部生成的文件。

对于选项(2),问题是hadoop流不区分\ n,\ r和\ r \ n换行符。由于snappy压缩最终会在压缩文件中传播这些字节代码,因此这是致命的。这是我的错误跟踪:

2013-04-03 22:29:50,194 WARN org.apache.hadoop.mapred.Child (main): Error running child
java.lang.RuntimeException: PipeMapRed.waitOutputThreads(): subprocess failed with code 1
    at org.apache.hadoop.streaming.PipeMapRed.waitOutputThreads(PipeMapRed.java:372)
    at org.apache.hadoop.streaming.PipeMapRed.mapRedFinished(PipeMapRed.java:586)
    at org.apache.hadoop.streaming.PipeMapper.close(PipeMapper.java:135)
    at org.apache.hadoop.mapred.MapRunner.run(MapRunner.java:57)
    ...

在hadoop的Java类上做了一些工作(例如,参见here),我们可能会解决\ r vs \ n \ n \ n \ n问题。但正如我最初所说,我的目标是在hadoop流模块中构建,而不涉及Java。有了这个限制,似乎没有办法解决这个问题。

最后,我回到了生成此群集正在使用的文件的人,并说服他们切换到gzip或lzo。

PS - 在选项(2)上,我玩弄了不同角色的分割记录(例如textinputformat.record.delimiter = X),但感觉非常hacky并且无论如何都没有用。

PPS - 另一种解决方法是编写脚本从S3下载文件,解压缩它们,然后运行-copyFromLocal将它们拉入HDFS。在计算上,这没有任何问题,但从工作流的角度来看,它会引入各种麻烦。

答案 1 :(得分:1)

假设您正在使用TextInputFormat(或其子类之一),则会自动处理扩展名为.snappy的压缩输入文件。

您可能需要考虑使用lzo压缩(.gz extenstion)而不是snappy。您放弃了一些压缩速度以获得更好的压缩率和一个可拆分的输入文件。 Cloudera提到了in their blog

  

需要注意的一点是,Snappy旨在与a一起使用   容器格式,如序列文件或Avro数据文件,而不是   例如,直接在纯文本上使用,因为后者是   不可拆分,不能使用MapReduce并行处理。   这与LZO不同,LZO可以索引压缩的LZO   用于确定拆分点的文件,以便可以处理LZO文件   有效地进行后续处理。