减少阶段后合并输出文件


75

在mapreduce中,每个化简任务将其输出写入名为part-r-nnnnn的文件,其中nnnnn是与化任务关联的分区ID。难道的map / reduce合并这些文件?如果是,怎么办?

Answers:


121

您可以通过调用以下方法来委托reduce输出文件的整个合并,而不必自己进行文件合并:

hadoop fs -getmerge /output/dir/on/hdfs/ /desired/local/output/file.txt

注意这将在本地合并HDFS文件。在运行之前,请确保您有足够的磁盘空间


16
有没有办法做到这一点,但在DFS上?我的意思是我想将它们合并到dfs上的单个文件中吗?
humanzz 2012年

10
dfs似乎不起作用,合并后的文件被写入本地文件系统。当然,您可以将其写回去,但看起来很浪费。
Marius Soutier 2014年

4
注意:这对于非文本文件并不安全。getMerge对文件进行简单的串联,使用SequenceFile之类的文件将不会给出合理的输出。
2014年

2
不能将HDFS用作目标位置。
Gaurav Kumar 2015年

getmerge将数据从hdfs带到本地。
armourbear

28

不,Hadoop无法合并这些文件。您获得的文件数量与减少任务的数量相同。

如果您需要将其作为下一份工作的输入,则不必担心会有单独的文件。只需将整个目录指定为下一个作业的输入即可。

如果您确实需要集群外部的数据,那么通常在从集群中提取数据时在接收端将它们合并。

即是这样的:

hadoop fs -cat /some/where/on/hdfs/job-output/part-r-* > TheCombinedResultOfTheJob.txt

感谢您的回答buf在map / reduce(mapred-default.xml)的配置文件中,有一个名为io.sort.factor的属性,它的作用是什么?
Shahryar

2
io.sort.factor与映射和reduce步骤之间的处理有关。没有减少的输出。
尼尔斯·巴耶斯

您如何知道part-r- *文件的合并顺序是正确的顺序?
拉兹万

3
@Razvan:顺序不重要。如果这确实很重要,那么您将拥有一个无法扩展的算法,并且您显然会对哪个Reducer完成了工作的哪一部分进行了假设。因此,如果发生这种情况,您将遇到另一种问题。
Niels Basjes '16

@NielsBasjes:最好使用“ hadoop fs -getmerge”而不是“ hadoop fs -cat”
娜迦,

8

这就是您可以用来在HDFS中合并文件的功能

public boolean getMergeInHdfs(String src, String dest) throws IllegalArgumentException, IOException {
    FileSystem fs = FileSystem.get(config);
    Path srcPath = new Path(src);
    Path dstPath = new Path(dest);

    // Check if the path already exists
    if (!(fs.exists(srcPath))) {
        logger.info("Path " + src + " does not exists!");
        return false;
    }

    if (!(fs.exists(dstPath))) {
        logger.info("Path " + dest + " does not exists!");
        return false;
    }
    return FileUtil.copyMerge(fs, srcPath, fs, dstPath, false, config, null);
}

8

对于仅文本文件,HDFS作为源文件和目标文件,请使用以下命令:

hadoop fs -cat /input_hdfs_dir/* | hadoop fs -put - /output_hdfs_file

这将串联所有文件,input_hdfs_dir并将输出写回到HDFS output_hdfs_file。请记住,尽管没有创建任何临时文件,但所有数据都将被带回到本地系统,然后再次上传到hdfs,这是使用UNIX pe进行的。

此外,这不适用于非文本文件,例如Avro,ORC等。

对于二进制文件,您可以执行以下操作(如果目录中映射了Hive表):

insert overwrite table tbl select * from tbl

根据您的配置,这还可能创建多个文件。要创建单个文件,请使用明确将reducer的数量设置为1mapreduce.job.reduces=1或将hive属性设置为hive.merge.mapredfiles=true


使用此解决方案时,还要注意可能的输入从stdin进入最终目的地。即,我遇到一种情况,即在启用了HA的群集中,当其中一个节点处于待机模式时,会出现一条警告消息。在那种情况下,我的输出包含了其他无害的警告消息。链接
kasur

4

part-r-nnnnn文件是在两者之间由“ r”指定的还原阶段之后生成的。现在的事实是,如果您运行一个减速器,您将有一个输出文件,例如part-r-00000。如果减速器的数量为2,那么您将拥有part-r-00000和part-r-00001,依此类推。看一下,如果hadoop框架已设计为可在Commodity Machines上运行,则输出文件太大而无法放入机器内存中,则该文件将被拆分。根据MRv1,逻辑上只能使用20个减速器。您可能有更多但需要在配置文件mapred-site.xml中自定义的内容。谈论你的问题;您可以使用getmerge或通过将以下语句嵌入驱动程序代码来将reducer的数量设置为1

job.setNumReduceTasks(1);

希望这能回答您的问题。


3

您可以运行其他map / reduce任务,其中map和reduce不会更改数据,分区程序会将所有数据分配给单个reducer。


1
如果您需要合并本地计算机无法处理的更多数据,则不需要
Havnar

1

除了我以前的回答,我还有几分钟想尝试的另一个答案。您可以使用CustomOutputFormat,它看起来像下面的代码

public class VictorOutputFormat extends FileOutputFormat<StudentKey,PassValue> {

    @Override
    public RecordWriter<StudentKey,PassValue> getRecordWriter(
            TaskAttemptContext tac) throws IOException, InterruptedException {
        //step 1: GET THE CURRENT PATH
        Path currPath=FileOutputFormat.getOutputPath(tac);

        //Create the full path
        Path fullPath=new Path(currPath,"Aniruddha.txt");

        //create the file in the file system
        FileSystem fs=currPath.getFileSystem(tac.getConfiguration());
        FSDataOutputStream fileOut=fs.create(fullPath,tac);
        return new VictorRecordWriter(fileOut);
    }

}

只是,从最后看第四行。我使用了自己的名称作为输出文件名,并使用15个reducer测试了该程序。文件仍然保持不变。因此,可以很清楚地得到一个输出文件而不是两个或更多文件,但输出文件的大小一定不能超过主内存的大小,即输出文件必须适合商用机器的内存,否则可能输出文件拆分出现问题。谢谢!!


getmerge可以解决您的目的,但这是另一种选择。但这很有用
Aniruddha Sinha 2015年

0

为什么不使用像这样的猪脚本来合并分区文件:

stuff = load "/path/to/dir/*"

store stuff into "/path/to/mergedir"

0

如果文件具有标题,则可以通过以下方法摆脱它:

hadoop fs -cat /path/to/hdfs/job-output/part-* | grep -v "header" > output.csv

然后手动为output.csv添加标题


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.