3

PostgreSQL で非常に大きなテーブル (300GB) からレポートを事前に生成しようとしています。私はこのようなことをします:

rs = stmt.executeQuery("SELECT * FROM tbl");
System.out.println("select all finished");
while (rs.next()) {
    /* generate report and save it in report table */
    /* generated reports are not in memory, 
     * They are saved in a summary table in each iteration */
}

アプリケーションを起動すると、Exception in thread "main" java.lang.OutOfMemoryError: Java heap space. 使用してみましたstmt.setFetchSize(1000)が、問題は解決しません。

解決策は何ですか?Debian 6.0.5 および openJDK 6 で PostgreSQL 8.4.11 を使用しています。

[アップデート]

出力されたスタック トレースは、OutOfMemoryError例外がrs = stmt.executeQuery("SELECT * FROM tbl");インラインで生成されたことを示しています。またSystem.out.println("select all finished");、表示されることはありません。

  1. モードで走っていautocommitます。
  2. stmt.getResultSetConcurrency()1007 を返します。
  3. stmt.getResultSetHoldability()2を返します。
  4. rs.getType()1003 を返します。
4

2 に答える 2

9

問題はおそらく、PostgreSQL が限らfetchSizeれた状況でのみ を使用することです。参照: http://jdbc.postgresql.org/documentation/91/query.html#fetchsize-example

  • サーバーへの接続には、V3 プロトコルを使用する必要があります。これは、サーバー バージョン 7.4 以降のデフォルトです (また、このバージョンでのみサポートされています)。
  • 接続は自動コミット モードであってはなりません。バックエンドはトランザクションの終了時にカーソルを閉じるため、自動コミット モードでは、バックエンドはカーソルから何かを取得する前にカーソルを閉じます。
  • Statement は、ResultSet.TYPE_FORWARD_ONLY の ResultSet タイプで作成する必要があります。これはデフォルトであるため、これを利用するためにコードを書き直す必要はありませんが、逆方向にスクロールしたり、ResultSet 内をジャンプしたりできないことも意味します。
  • 指定されたクエリは、セミコロンでつながれた複数のステートメントではなく、単一のステートメントである必要があります。

したがって、自動コミットでこれを実行している場合、または PostgreSQL 以外の結果セット タイプを使用している場合、TYPE_FORWARD_ONLYすべての行がフェッチされます。また、PostgreSQL JDBC 9.0-801 ドライバーのソースを見ると、保持可能な結果セットを使用すると、すべての行がフェッチされるように見えます。

于 2012-06-09T07:38:18.997 に答える
0

そこにあるものがそのようなエラーを引き起こすとは思いません。ガベージ コレクターは を反復処理するときに発生すると思いrs.next()ます。そのため、メモリの問題は発生しないはずです。おそらく、結果セットでやろうとしていることと関係があります。あなたが何をしているのか正確にはわかりませんが、オブジェクトのすべてをメモリに保存しようとしているとしか思えません。そのため、値を aStringBuilderまたは何かに格納すると問題が発生します。結果をすべてメモリ内のオブジェクトに収集しようとするのではなく、結果をディスクに書き込むことをお勧めします (繰り返しますが、そのような情報を提供していないため、何をしているのかを推測しているだけです)。Java Helper Libraryには、resultSetToCSVFile(ResultSet rs, String destination)役立つと思われる方法。このように処理すると、すべてをメモリに保持することはできなくなりますが、目的のレポートを作成することはできます。ちなみに、これを行うには、opencsv ライブラリを含める必要があります。または、 Java Helper Libraryをインクルードしてメソッドを直接呼び出すこともできます。

 /**
   * Prints the given ResultSet to a comma separated file (the destination)
   *
   * @param rs
   * @param destination
   * @throws SQLException
   * @throws FileNotFoundException
   */
  public static void resultSetToCSVFile(ResultSet rs, String destination) throws SQLException, FileNotFoundException, IOException {
    ResultSetMetaData metaData = rs.getMetaData();
    int columnCount = metaData.getColumnCount();
    String[] header = new String[columnCount];
    for (int i = 0; i < columnCount; i++) {
      header[i] = metaData.getColumnName(i + 1);
    }
    File file = new File(destination);
    IOHelper.checkDirectory(file);
    try (PrintWriter pw = new PrintWriter(file); CSVWriter writer = new CSVWriter(pw)) {
      writer.writeNext(header);
      while (rs.next()) {
        String[] row = new String[columnCount];
        for (int i = 0; i < columnCount; i++) {
          String string = rs.getString(i + 1);
          if (string == null) {
            string = "";
          }
          row[i] = string;
        }
        writer.writeNext(row);
      }
    }
  }
于 2012-06-09T05:34:44.570 に答える