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

StackOverflow https://stackoverflow.com/questions/1107732

Вопрос

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

Если я просто выдам команду "ВЫБРАТЬ * ОТ клиента", это может вернуть огромное количество данных, которые в конечном итоге вызовут ООМ.Интересно, есть ли способ, которым я могу обработать запись сразу, как только она станет доступной, и удалить запись из памяти после этого во время процесса извлечения sql?

--- отредактировано 13 июля 2009

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

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

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

Это было полезно?

Решение

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

Другие советы

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

Если вы используете 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);

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

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

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

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

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, не используются
  • Ничто не закрыто
  • Генерируется общее исключение
  • Это основной метод
  • Использование print для генерации XML
  • Специфичный для Oracle SQL
  • Обычный текстовый пароль
  • Некоторые столбцы выглядят неудобно в строковом представлении
  • UTF-8 слишком интернациональен
  • Объем XML-структуры велик

На каком этапе возникает ошибка ООМ, происходит ли это при извлечении данных или обработке данных в XML-файле?

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

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

java MyConverter > results.txt

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top