• MR案例:小文件处理方案


    HDFS被设计来存储大文件,而有时候会有大量的小文件生成,造成NameNode资源的浪费,同时也影响MapReduce的处理效率。有哪些方案可以合并这些小文件,或者提高处理小文件的效率呢?

    1). 所有HDFS小文件数据导出到本地单个文件后,再存入HDFS

    [root@ncst ~]# hadoop fs -ls /test/in/small/
    Found 3 items
    -rw-r--r--   1 root supergroup          1 2015-08-25 22:17 /test/in/small/small.1
    -rw-r--r--   1 root supergroup          1 2015-08-25 22:17 /test/in/small/small.2
    -rw-r--r--   1 root supergroup          1 2015-08-25 22:17 /test/in/small/small.3

     1.1). 利用hadoop fs -cathadoop fs -text命令,将所有内容导出到本地文件,然后put到HDFS即可。如:

    [root@ncst test]# hadoop fs -cat /test/in/small/small.* > small_data
    [root@ncst test]# hadoop fs -put small_data /test/in/small/

     1.2). 或者使用管道

    [root@ncst test]# hadoop fs -cat /test/in/small/small.* | 
    > hadoop fs -put - /test/in/small/small_data

       最后删除原有文件,注意避免删除新上传的FlumeData1,通过模糊匹配的方式即可

    shell> hadoop fs -rm -skipTrash /test/in/small/small.*

    总结:

    • 这个合并方案适用于文件格式一致,文件合并顺序不敏感(或者按照文件名为序)的场景,例如这里收集的日志信息,每一条都是一样的格式,每一条记录本身有生成时间信息,所以不依赖与在文件中的位置。
    • 如果文件中使用数字用于命名,而期望以数字顺序而不是字符串顺序进行合并,会遇到如下问题:-text包含-cat的功能,-cat只能针对平面文件,而-text可以处理压缩(compressed)和顺序(sequence)文件。
      1. 问题:1,10,100,1000,11,110.将这些数字进行排列。如果按照字符串顺序,是1,10,100,1000,11,110,而我们知道数字的期望顺序是1,10,11,100,110,1000。
      2. 这里的一个参考方法可以如下: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 以此类推实现更多位数的数字排序。
      3. 如果可以的话,使用数字前补零的命名方式(如000009),使得所有文件名称长度一致,可以使得字符顺序与数字的顺序一致。

    2). 调用现有API方法 自行开发

    本质上,这种方案还是先把数据内容读到客户端,再写入到HDFS。

     2.1). org.apache.hadoop.fs.FileUtil.copyMerge()方法将指定目录下的所有文件拷贝、合并到一个文件。copyMerge()可以在不同FileSystem中移动,通过deleteSource标识来指定是否删除,如果设定为true,则会删除整个srcDir目录。而conf的传入其实只是为了获取 io.file.buffer.size 的设置。而 addString 则是在合并时,每个文件后添加的字符串。

      /** Copy all files in a directory to one output file (merge). */
      public static boolean copyMerge(FileSystem srcFS, Path srcDir, 
                                      FileSystem dstFS, Path dstFile, 
                                      boolean deleteSource,
                                      Configuration conf, String addString) throws IOException {
        //检查hdfs上输出路径是否存在
        dstFile = checkDest(srcDir.getName(), dstFS, dstFile, false);
    
        if (!srcFS.getFileStatus(srcDir).isDirectory())
          return false;
       
        OutputStream out = dstFS.create(dstFile);
        
        try {
          FileStatus contents[] = srcFS.listStatus(srcDir);
          Arrays.sort(contents);
          for (int i = 0; i < contents.length; i++) {
            if (contents[i].isFile()) {
              InputStream in = srcFS.open(contents[i].getPath());
              try {
                IOUtils.copyBytes(in, out, conf, false);
                if (addString!=null)
                  out.write(addString.getBytes("UTF-8"));
                    
              } finally {
                in.close();
              } 
            }
          }
        } finally {
          out.close();
        }    
        //是否删除原有文件
        if (deleteSource) {
          return srcFS.delete(srcDir, true);
        } else {
          return true;
        }
     }  

     2.2). 参考copyMerge()的写法,自定义合并程序。如下例,在本FileSystem中将srcDir下的所有文件写入同一个文件dstFile,而删除则是针对被合并的文件而不是整个目录。

    public boolean dirMergeToFile(String srcDir, Path dstFile, boolean deleteSource){
            boolean rtcd = true;
            try {
                Configuration conf = new Configuration();
                FileSystem fs = FileSystem. get(conf);
    
                Path sDir = new Path(srcDir);
                Path dFile = dstFile;
                if (!fs.getFileStatus(sDir).isDirectory()) {
                    System. out.println(sDir.getName() + " is not a directory!");
                    return false ;
                }
                OutputStream out = null;
                try {
                    //排除隐藏的文件,即以.开头。
                    FileStatus contents[] = fs.listStatus(sDir);
    
                    if(contents.length == 0){
                        return true ;
                    }
    
                    if (fs.exists(dFile)) {
                        System. out.println(dFile.getName() + " exists!");
                        return false ;
                    }
    
                    out = fs.create(dFile);
                    Arrays.sort(contents);
    
                    for (int i = 0; i < contents.length; i++) {
                        if (contents[i].isFile()) {
                            InputStream in = fs.open(contents[i].getPath());
                            try {
                                IOUtils.copyBytes(in, out, conf, false );
                            } finally {
                                in.close();
                            }
                            if (deleteSource && !fs.delete(contents[i].getPath(), false)) {
                                rtcd = false;
                            }
                        }
                    }
                } finally {
                    if (out != null)
                        out.close();
                }
                return rtcd;
            } catch (IOException e) {
                System. out.println(e.getMessage());
                return false ;
            }
        }

    3). Hadoop自带方案Hadoop Archive

    hadoop archive 命令运行MapReduce job来并行处理输入文件,将小文件的内容合并形成少量大文件,然后再利用 index 文件,指出小文件在大文件中所属的坐标,以此来减少小文件的数量。Hadoop Archives生成归档文件格式为HAR。详见解读:Hadoop Archive

    4). Sequence File

    Sequence File由一系列二进制的键值对组成,其中key为小文件的名字,value的File Content。创建Sequence File的过程可以使用MapReduce Job完成。Sequence Files也是splittable的,所以 MapReduce 可以break them into chunks,并且分别被独立的处理。和HAR不同的是,这种方式还支持压缩。block压缩在许多情况下都是最好的选择,因为它将多个records压缩到一起,而不是一个record一个压缩。详见MR案例:小文件合并SequeceFile

    5). CombineFileInputFormat类

    CombineFileInputFormat是Hadoop自带的多文件合并处理方案。指定输入目录,将其下的大量小文件进行合并分片,达到减少map任务数量的目的。详细见解读:CombineFileInputFormat类

  • 相关阅读:
    sql
    字符和字符串处理例子
    如何用火狐设置代理
    数组指针的一个小例子
    (转)数组指针和指针数组的区别
    函数
    (转)C语言指针5分钟教程
    通俗版解释网关,IP地址,ARP欺骗,DDOS攻击
    计算网络地址
    计算机网络性能指标
  • 原文地址:https://www.cnblogs.com/skyl/p/4732318.html
Copyright © 2020-2023  润新知