Как избежать ошибки OOM (вне памяти) при извлечении всех записей из огромной таблицы?

Мне поручено преобразовать огромную таблицу в пользовательский XML файл. Я буду использовать Java для этой работы.

Если я просто выдаю запрос "SELECT * FROM", он может вернуть огромное количество данных, которые в конечном итоге вызывают OOM. Интересно, есть ли способ, с помощью которого я могу обработать запись сразу после ее появления и удалить запись из памяти после этого во время процесса получения sql?

--- отредактировано 13.07.2009

Позвольте мне уточнить мой вопрос. У меня 1 сервер сервера и 1 сервер приложений. Когда я выдаю запрос выбора в приложении, данные будут перемещаться с сервера db на сервер приложений.

Я верю (исправьте меня, если я ошибаюсь) ResultSet должен будет дождаться получения всех записей в запросе. Даже если мы установили размер выборки как 4, для таблицы с 1000 записями, у нас все еще есть 1000 записей в кучевой памяти сервера приложений, правильно ли это? Размер Fetch влияет только на количество обратных рейсов с/на сервер db.

Мой вопрос в том, как начать обработку на этом 4 (или любом количестве) записей сразу после его поступления на сервер приложений и избавить его от освобождения памяти на сервере приложений?

Ответы

Ответ 1

Думаю, вы можете использовать то же решение, что этот. Прокручиваемый набор результатов.

Ответ 2

С более подробной информацией я могу получить более полезный ответ.

Если вы используете MySQL:

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
       java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);

из http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.html:

java.util.Properties info = new java.util.Properties();
info.put ("user", "scott");
info.put ("password","tiger");
info.put ("defaultRowPrefetch","15");
getConnection ("jdbc:oracle:oci:@",info);

Ответ 3

Если вы используете JDBC, вы можете использовать ResultSet с курсором, который вы перебираете по одной записи за раз. Вы должны убедиться, что тогда вы записываете свой XML файл в файл по одной записи за раз, а не с помощью DOM для создания XML.

Ответ 4

Одно эмпирическое правило, которое я узнал из моего опыта, заключается в том, что вы НИКОГДА не приводите ВСЕ данные из базы данных на ваш сервер приложений. Одна вещь, которую вы можете сделать, это реализовать процедуру для размещения своих данных.

Вы можете привести одну страницу данных, содержащих около 1000-5000 записей, обработать их, а затем снова извлечь данные для следующей страницы.

Ответ 5

Концепция экспорта всей таблицы. (Примечание для экспертов: я знаю о его недостатках.)

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
public class FullTableExport {
    public static String toXML(String s) {
        if (s != null) {
            StringBuilder b = new StringBuilder(s.length());
            for (int i = 0, count = s.length(); i < count; i++) {
                char c = s.charAt(i);
                switch (c) {
                case '<':
                    b.append("&lt;");
                    break;
                case '>':
                    b.append("&gt;");
                    break;
                case '\'':
                    b.append("&#39;");
                    break;
                case '"':
                    b.append("&quot;");
                    break;
                case '&':
                    b.append("&amp;");
                    break;
                default:
                    b.append(c);
                }
            }
            return b.toString();
        }
        return "";
    }
    public static void main(String[] args) throws Exception {
        String table = "CUSTOMER";
        int batch = 100;

        Class.forName("oracle.jdbc.driver.OracleDriver");
        Connection conn = DriverManager.getConnection(
            "jdbc:oracle:thin:@server:orcl", "user", "pass");
        PreparedStatement pstmt = conn.prepareStatement(
            "SELECT /*+FIRST_ROWS(" + batch + ") */ * FROM " + table);
        ResultSet rs = pstmt.executeQuery();
        rs.setFetchSize(batch);
        ResultSetMetaData rsm = rs.getMetaData();
        File output = new File("result.xml");
        PrintWriter out = new PrintWriter(new BufferedWriter(
            new OutputStreamWriter(
            new FileOutputStream(output), "UTF-8")), false);
        out.printf("<?xml version='1.0' encoding='UTF-8'?>%n");
        out.printf("<table name='%s'>%n", toXML(table));
        int j = 1;
        while (rs.next()) {
            out.printf("\t<row id='%d'>%n", j++);
            for (int i = 1; i <= rsm.getColumnCount(); i++) {
                out.printf("\t\t<col name='%s'>%s</col>%n", 
                    toXML(rsm.getColumnName(i)), 
                    toXML(rs.getString(i)));
            }
            out.printf("\t</row>%n");
        }
        out.printf("</table>%n", table);
        out.flush();
    }
}

Изменить Недостатки (спасибо @J.S.):

  • Внешние библиотеки не используются за пределами ojdbc
  • Ничего не закрыто
  • Генерированное исключение выбрано
  • Это основной метод
  • Использование печати для генерации XML
  • Oracle специфический SQL
  • Обычный текстовый пароль
  • Некоторые столбцы выглядят неудобно в строчном представлении
  • UTF-8 является слишком международным
  • Размер структуры XML большой

Ответ 6

На каком этапе происходит ошибка OOM, находится ли она в поиске или обработке данных в XML файле?

Если его извлечение данных, получите данные в пакетах. Сначала выведите общее количество строк, закажите выделение с помощью первичного ключа и ограничьте количество строк, выбранных для жевательных размеров.

Если при создании файла XML отправьте XML node каждого клиента в System.out.println, не храните его в памяти. Запустите программу через запятую и переадресуйте весь вывод в файл;

java MyConverter > results.txt

По мере того, как вы зацикливаете запись, все сохраняется в файле.