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 及更高版本的默认设置(仅受支持)。
  • 连接不得处于自动提交模式。后端在事务结束时关闭游标,因此在自动提交模式下,后端将在可以从中获取任何内容之前关闭游标。
  • 必须使用 ResultSet.TYPE_FORWARD_ONLY 的 ResultSet 类型创建语句。这是默认设置,因此无需重写代码即可利用这一点,但这也意味着您不能向后滚动或以其他方式在 ResultSet 中跳转。
  • 给出的查询必须是单个语句,而不是用分号串在一起的多个语句。

因此,如果您在自动提交中执行此操作,或者使用TYPE_FORWARD_ONLYPostgreSQL 以外的结果集类型将获取所有行。还查看 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 回答