【问题标题】:Uploading large gzipped data files to HDFS将大型 gzip 压缩数据文件上传到 HDFS
【发布时间】:2011-06-22 16:23:33
【问题描述】:

我有一个用例,我想在 HDFS 上上传大的 gzip 压缩文本数据文件(约 60 GB)。

我下面的代码需要大约 2 个小时才能以 500 MB 的块上传这些文件。以下是伪代码。我正在检查是否有人可以帮助我减少这个时间:

i) int fileFetchBuffer = 500000000; System.out.println("文件获取缓冲区为:" + fileFetchBuffer); 整数偏移 = 0; int bytesRead = -1;

    try {
        fileStream = new FileInputStream (file);    
        if (fileName.endsWith(".gz")) {
            stream = new GZIPInputStream(fileStream);

            BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); 

            String[] fileN = fileName.split("\\.");
            System.out.println("fil 0 : " + fileN[0]);
            System.out.println("fil 1 : " + fileN[1]);
            //logger.info("First line is: " + streamBuff.readLine());

            byte[] buffer = new byte[fileFetchBuffer];

            FileSystem fs = FileSystem.get(conf);

            int charsLeft = fileFetchBuffer;
            while (true) {

                charsLeft = fileFetchBuffer;    



             logger.info("charsLeft outside while: " + charsLeft);

          FSDataOutputStream dos = null;
                while (charsLeft != 0) {
                    bytesRead = stream.read(buffer, 0, charsLeft);
                    if (bytesRead < 0) {
                        dos.flush();
                        dos.close();
                        break;
                    }
                    offset = offset + bytesRead;
                    charsLeft = charsLeft - bytesRead; 
                    logger.info("offset in record: " + offset);
                    logger.info("charsLeft: " + charsLeft);
                    logger.info("bytesRead in record: " + bytesRead);
                    //prettyPrintHex(buffer);

                    String outFileStr = Utils.getOutputFileName(
                            stagingDir,
                            fileN[0],
                            outFileNum);

                    if (dos == null) {
                    Path outFile = new Path(outFileStr);
                    if (fs.exists(outFile)) {
                        fs.delete(outFile, false);
                    }

                    dos = fs.create(outFile);
                    }

                    dos.write(buffer, 0, bytesRead);


                } 

                logger.info("done writing: " + outFileNum);
                dos.flush();
                dos.close();

                if (bytesRead < 0) {
                    dos.flush();
                    dos.close();
                    break;
                }

                outFileNum++;

            }  // end of if


        } else {
            // Assume uncompressed file
            stream = fileStream;
        }           

    } catch(FileNotFoundException e) {
        logger.error("File not found" + e);
    }

【问题讨论】:

    标签: java hadoop hdfs gzipinputstream


    【解决方案1】:

    您应该考虑使用super package IO from Apache

    它有一个方法

    IOUtils.copy( InputStream, OutputStream )
    

    这将大大减少复制文件所需的时间。

    【讨论】:

    • @Snicolas - 如何拆分 InputStream ?例如,60 Gb 必须以 1 Gb 块上传。这个函数如何知道从 InputStream 中的哪个位置复制?
    • 您可以考虑继承 FilterInputStream 以创建一个新类,该类从某个偏移量读取您的原​​始输入流,并且不超过 1Gb。
    • 另一种选择可能是使用 FileChannels 和方法 transferTo,这也将非常有效。
    • @Snicolas - 我如何在压缩输入流中创建文件通道?
    • @user656189 您需要上传解压缩的文件吗?或者您只想将 60 Gb 的压缩文件放入 1 Gb 的切片中?这不是同一个问题。
    【解决方案2】:

    我尝试使用缓冲输入流并没有发现真正的区别。 我想文件通道实现可能会更有效。如果还不够快,请告诉我。

    package toto;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class Slicer {
    
        private static final int BUFFER_SIZE = 50000;
    
        public static void main(String[] args) {
    
            try 
            {
                slice( args[ 0 ], args[ 1 ], Long.parseLong( args[2]) );
            }//try
            catch (IOException e) 
            {
                e.printStackTrace();
            }//catch
            catch( Exception ex )
            {
                ex.printStackTrace();
                System.out.println( "Usage :  toto.Slicer <big file> <chunk name radix > <chunks size>" );
            }//catch
        }//met
    
        /**
         * Slices a huge files in chunks.
         * @param inputFileName the big file to slice.
         * @param outputFileRadix the base name of slices generated by the slicer. All slices will then be numbered outputFileRadix0,outputFileRadix1,outputFileRadix2...
         * @param chunkSize the size of chunks in bytes
         * @return the number of slices.
         */
        public static int slice( String inputFileName, String outputFileRadix, long chunkSize ) throws IOException
        {
            //I would had some code to pretty print the output file names
            //I mean adding a couple of 0 before chunkNumber in output file name
            //so that they all have same number of chars
            //use java.io.File for that, estimate number of chunks, take power of 10, got number of leading 0s
    
            //just to get some stats
            long timeStart = System.currentTimeMillis();
            long timeStartSlice = timeStart;
            long timeEnd = 0;
    
            //io streams and chunk counter
            int chunkNumber = 0;
            FileInputStream fis = null;
            FileOutputStream fos = null;
    
            try 
            {
                //open files
                fis = new FileInputStream( inputFileName );
                fos = new FileOutputStream( outputFileRadix + chunkNumber );
    
                //declare state variables
                boolean finished = false;
                byte[] buffer = new byte[ BUFFER_SIZE ];
                int bytesRead = 0;
                long bytesInChunk = 0;
    
    
                while( !finished )
                {
                    //System.out.println( "bytes to read " +(int)Math.min( BUFFER_SIZE, chunkSize - bytesInChunk ) );
                    bytesRead = fis.read( buffer,0, (int)Math.min( BUFFER_SIZE, chunkSize - bytesInChunk ) );
    
                    if( bytesRead == -1 )
                        finished = true;
                    else
                    {
                                                fos.write( buffer, 0, bytesRead );
                        bytesInChunk += bytesRead;
                        if( bytesInChunk == chunkSize )
                        {
                            if( fos != null )
                            {
                                fos.close();
                                timeEnd = System.currentTimeMillis();
                                System.out.println( "Chunk "+chunkNumber + " has been generated in "+ (timeEnd - timeStartSlice) +" ms");
                                chunkNumber ++;
                                bytesInChunk = 0;
                                timeStartSlice = timeEnd;
                                System.out.println( "Creating slice number " + chunkNumber );
                                fos = new FileOutputStream( outputFileRadix + chunkNumber );
                            }//if
                        }//if
                    }//else
                }//while
            }
            catch (Exception e) 
            {
                System.out.println( "A problem occured during slicing : " );
                e.printStackTrace();
            }//catch
            finally 
            {
                //whatever happens close all files
                System.out.println( "Closing all files.");
                if( fis != null )
                    fis.close();
                if( fos != null )
                    fos.close();
            }//fin
    
            timeEnd = System.currentTimeMillis();
            System.out.println( "Total slicing time : " + (timeEnd - timeStart) +" ms" );
            System.out.println( "Total number of slices "+ (chunkNumber +1) );
    
            return chunkNumber+1;
        }//met
    }//class
    

    您好, 斯蒂芬

    【讨论】:

    • @Snicolas - 非常感谢您花时间做这个!这和我的解决方案不一样吗?或者这样会更快。
    • 速度非常快,我不解压缩 60 Gb 的 tar 文件...请试一试。如果我的时间更好,请接受我的回答;)
    • 我会尝试,但问题是它会在行边界上分割吗?
    • 不,它只是对文件进行切片。由于我不解压缩文件,因此无法在任何边界上对其进行切片,您只需获得相等且预定义大小的切片。我以为这就是我们昨天达成的共识。这是我能做的最好的了。
    • @Snicolas - 我同意。我会运行它。我能感觉到它会更快。那么,基本上现在我们已经压缩了切片了吗?
    猜你喜欢
    • 1970-01-01
    • 2017-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-26
    • 2021-04-23
    • 2018-07-06
    相关资源
    最近更新 更多