2

お気遣いありがとうございます。

Java を使用して、大量のデータ、実際には大量のデータ (600 万行) を .csv ファイルにエクスポートしたいと考えています。このアプリは、JPA を使用し、toplink (ojdbc14) を使用するスイング アプリケーションです。

私は使用しようとしました:

BufferedWriter RandomAccessFile FileChannel

などなど、メモリの消費量が非常に多いままで、最大ヒープ サイズを 800m (-Xmx800m) に設定しても、Java ヒープのメモリ不足例外が発生します。

ソースコードの私の最後のバージョン:

...(more lines of code)

FileChannel channel = getRandomAccessFile(tempFile).getChannel();
Object[][] data = pag.getRawData(); //Database data in a multidimentional array

            for (int j = 0; j < data.length; j++) {
                write(data[j], channel); //write data[j] (an array) into the channel
                freeStringLine(data[j]); //data[j] is an array, this method sets all positions =null
                data[j] = null;//sets reference in null
            }

            channel.force(false); //force writing in file system (HD)
            channel.close(); //Close the channel
            pag = null; 

...(more lines of code)

 private void write(Object[] row, FileChannel channel) throws DatabaseException {
    if (byteBuff == null) {
        byteBuff = ByteBuffer.allocateDirect(1024 * 1024);
    }
    for (int j = 0; j < row.length; j++) {
        if (j < row.length - 1) {
            if (row[j] != null) {
                byteBuff.put(row[j].toString().getBytes());
            }
            byteBuff.put(SPLITER_BYTES);
        } else {
            if (row[j] != null) {
                byteBuff.put(row[j].toString().getBytes());
            }
        }
    }
    byteBuff.put("\n".toString().getBytes());        
    byteBuff.flip();
    try {
        channel.write(byteBuff);
    } catch (IOException ex) {
        throw new DatabaseException("Imposible escribir en archivo temporal de exportación : " + ex.getMessage(), ex.getCause());
    }
    byteBuff.clear();
}

600 万行あるので、ファイルの作成中にそのデータをメモリに保存したくありません。多くの一時ファイル (それぞれ 5000 行) を作成し、プロセスの最後に、2 つの FileChannel を使用して、これらすべての一時ファイルを 1 つのファイルに追加しました。ただし、参加前にメモリ不足の例外が起動されます。

大量のデータをエクスポートするための別の戦略はありますか?

回答ありがとうございます。私の英語でごめんなさい、私はxDを改善しています

4

1 に答える 1

3

答えは、「ストリーム」アプローチを使用することです。つまり、データセットをスクロールしながら、1 行を読み取り、1 行を書き込みます。結果セット全体を取得するのではなく、クエリ結果をカーソルとして取得し、それを反復処理する必要があります。

JPA では、次のようなコードを使用します。

ScrollableResults cursor = session.createQuery("from SomeEntity x").scroll();

while (cursor.next()) {
    writeToFile(cursor);
}

これは、一度にメモリ内に 1 つの行しかないことを意味します。これは、任意の数の行に完全にスケーラブルであり、最小限のメモリしか使用しません (とにかく高速です)。

結果セット内のすべての行を一度に取得することは、小さな結果セット (ほとんどの場合) で機能する便利なアプローチですが、いつものように、便利さには代償が伴い、すべての状況で機能するとは限りません。

于 2011-08-09T21:16:59.110 に答える