合并hdfs文件

时间:2013-02-12 11:08:17

标签: hadoop hdfs

我在HDFS中有1000多个文件,命名约定为1_fileName.txtN_fileName.txt。每个文件的大小为1024 MB。 我需要将这些文件合并为一个(HDFS)并保持文件的顺序。说5_FileName.txt只应在4_fileName.txt

之后追加

执行此操作的最佳和最快方法是什么。

是否有任何方法可以在不复制数据节点之间的实际数据的情况下执行此合并?对于e-g:获取这些文件的块位置,并在Namenode中创建一个具有这些块位置的新条目(FileName)?

5 个答案:

答案 0 :(得分:18)

没有有效的方法,您需要将所有数据移动到一个节点,然后再移回HDFS。

执行此操作的命令行scriptlet可能如下所示:

hadoop fs -text *_fileName.txt | hadoop fs -put - targetFilename.txt

这会将所有匹配glob的文件捕获到标准输出,然后您将该流传输到put命令并将该流输出到名为targetFilename.txt的HDFS文件

你唯一的问题是你已经去过的文件名结构 - 如果你有固定的宽度,zeropadded数字部分会更容易,但在它的当前状态你会得到一个意想不到的lexigraphic顺序(1,10, 100,1000,11,110等)而不是数字顺序(1,2,3,4等)。您可以通过将scriptlet修改为:

来解决此问题
hadoop fs -text [0-9]_fileName.txt [0-9][0-9]_fileName.txt \
    [0-9][0-9[0-9]_fileName.txt | hadoop fs -put - targetFilename.txt

答案 1 :(得分:12)

有一种API方法org.apache.hadoop.fs.FileUtil.copyMerge可以执行此操作:

public static boolean copyMerge(
                    FileSystem srcFS,
                    Path srcDir,
                    FileSystem dstFS,
                    Path dstFile,
                    boolean deleteSource,
                    Configuration conf,
                    String addString)

按字母顺序读取srcDir中的所有文件,并将其内容附加到dstFile。

答案 2 :(得分:5)

如果你可以使用火花。它可以像

那样完成
sc.textFile("hdfs://...../part*).coalesce(1).saveAsTextFile("hdfs://...../filename)

希望这有效,因为spark以分布式方式工作,你不必将文件复制到一个节点。虽然只是一个小心,但如果文件非常大,火花中的合并文件可能会很慢。

答案 3 :(得分:1)

由于文件顺序很重要且词典顺序不能达到目的,因此它似乎是为这项任务编写映射器程序的好选择,它可能会定期运行。 在没有reducer的情况下,将其写为HDFS map任务是有效的,因为它可以将这些文件合并到一个输出文件中,而不会跨数据节点移动太多数据。由于源文件是在HDFS中,并且由于映射器任务将尝试数据关联,因此它可以合并文件而无需跨不同数据节点移动文件。

映射器程序需要一个自定义的InputSplit(在输入目录中获取文件名并根据需要对其进行排序)和一个自定义的InputFormat。

映射器可以使用hdfs append,也可以使用原始输出流,它可以在byte []中写入。

我想到的Mapper程序的草图如下:

public class MergeOrderedFileMapper extends MapReduceBase implements Mapper<ArrayWritable, Text, ??, ??> 
{
    FileSystem fs;

    public void map(ArrayWritable sourceFiles, Text destFile, OutputCollector<??, ??> output, Reporter reporter) throws IOException 
    {

        //Convert the destFile to Path.
        ...
        //make sure the parent directory of destFile is created first.
        FSDataOutputStream destOS = fs.append(destFilePath);
        //Convert the sourceFiles to Paths.
        List<Path> srcPaths;
        ....
        ....
            for(Path p: sourcePaths) {

                FSDataInputStream srcIS = fs.open(p);
                byte[] fileContent
                srcIS.read(fileContent);
                destOS.write(fileContent);
                srcIS.close();
                reporter.progress();  // Important, else mapper taks may timeout.
            }
            destOS.close();


        // Delete source files.

        for(Path p: sourcePaths) {
            fs.delete(p, false);
            reporter.progress();
        }

    }
}

答案 4 :(得分:0)

我经常使用PySpark编写实现。

以Hadoop的copyMerge()为模型,并使用相同的较低级Hadoop API来实现这一目标。

https://github.com/Tagar/abalon/blob/v2.3.3/abalon/spark/sparkutils.py#L335

它保持文件名的字母顺序。