读取大表数据时出现java.lang.OutOfMemoryError错误

4

我正在尝试从一个非常大的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上使用PostgreSQL 8.4.11和openJDK 6。 [更新] 打印的堆栈跟踪显示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。

你真的需要所有的列吗? - mechanical_meat
解决方案是使用更少的内存。 - K-ballo
结果集类型和可保持性是什么?你是在执行时使用自动提交还是手动提交? - Mark Rotteveel
2个回答

10
问题可能在于PostgreSQL只在一组狭窄的情况下使用fetchSize。请参见:http://jdbc.postgresql.org/documentation/91/query.html#fetchsize-example
  • 服务器连接必须使用V3协议。这是服务器版本7.4及更高版本的默认值,也是唯一支持的。
  • 连接不能处于自动提交模式。后端在事务结束时关闭游标,因此在自动提交模式下,后端将在任何内容被获取之前关闭游标。
  • 结果集类型必须为ResultSet.TYPE_FORWARD_ONLY。这是默认值,因此不需要重写任何代码,但这也意味着您无法向后滚动或以其他方式在结果集中跳转。
  • 给定的查询必须是单个语句,而不是用分号串起来的多个语句。
因此,如果您在自动提交中执行此操作,或者使用除TYPE_FORWARD_ONLY之外的结果集类型,则PostgreSQL将提取所有行。此外,查看PostgreSQL JDBC 9.0-801驱动程序的源代码,似乎使用可保留的结果集也将使其提取所有行。

0
我认为你所写的代码不会导致这种类型的错误。我相信垃圾回收器会在你迭代rs.next()时进行,因此你不应该有内存问题。这可能与你尝试对结果集进行的操作有关。但是,如果不知道你具体在做什么,我只能猜测你正在尝试将所有内容存储在内存中的对象中。因此,如果你将值存储到StringBuilder或其他类似的对象中,那就会出现问题。我建议你在进行操作时将结果写入磁盘,而不是尝试将其全部收集到内存中的对象中(再次强调,我只是猜测你正在做什么,因为你没有提供这种信息)。在Java Helper Library中,有一个resultSetToCSVFile(ResultSet rs, String destination)方法,你可能会发现它很有用。以这种方式处理可以防止你将所有内容都保存在内存中,但你仍然可以生成你想要的报告。顺便说一下,要实现这个功能,你需要包含opencsv library。或者你可以直接包含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);
      }
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接