2012-06-09 3 views
3

Я пытаюсь предварительно генерировать отчеты из очень большой таблицы (300 ГБ) в PostgreSQL. Я делаю что-то вроде этого:java.lang.OutOfMemoryError при чтении из большой таблицы

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) Но это не решает проблему.

Какое решение? Я использую PostgreSQL 8.4.11 на Debian 6.0.5 и OpenJDK 6.

[UPDATE]

Напечатанный трассировки стека показывает, что 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.
+0

Вам действительно нужны все столбцы ...? – bernie

+0

Решение использует меньше памяти –

+0

Каков тип результатов и устойчивость? Выполняется ли с помощью автоматической фиксации или без нее? –

ответ

8

Проблема заключается в том, что, вероятно, PostgreSQL использует только fetchSize в узком наборе обстоятельств. См: http://jdbc.postgresql.org/documentation/91/query.html#fetchsize-example

  • соединение с сервером должны использовать протокол V3. Это значение по умолчанию для (и поддерживается только) версиями версий 7.4 и более поздних версий.
  • Подключение не должно быть в автоматическом режиме. Бэкэнд закрывает курсоры в конце транзакций, поэтому в режиме autocommit бэкэнд будет закрывать курсор, прежде чем что-либо можно извлечь из него.
  • Заявление должно быть создано с помощью типа ResultSet ResultSet.TYPE_FORWARD_ONLY. Это значение по умолчанию, поэтому для его использования не требуется переписывать код, но это также означает, что вы не можете прокручивать назад или иначе прыгать в ResultSet.
  • Указанный запрос должен быть одним утверждением, а не несколькими операторами, связанными с точками с запятой.

Так что, если вы выполняете это в автоматической фиксации, или с типом Resultset, кроме 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); 
     } 
    } 
    } 
+0

проверить наличие обновлений –

Смежные вопросы