簡體   English   中英

從大表中檢索所有記錄時如何避免OOM(Out of memory)錯誤?

[英]How to avoid OOM (Out of memory) error when retrieving all records from huge table?

我有一個任務是將一個巨大的表轉換為自定義XML文件。 我將使用Java來完成這項工作。

如果我只是發出“SELECT * FROM customer”,它可能會返回最終導致OOM的大量數據。 我想知道,有沒有一種方法可以在記錄可用后立即處理,並在sql檢索過程中從內存中刪除記錄?

---於2009年7月13日編輯

讓我詳細說明我的問題。 我有1個db服務器和1個應用服務器。 當我在應用程序中發出選擇查詢時,數據將從數據庫服務器傳輸到應用程序服務器。

我相信(如果我錯了,請糾正我)ResultSet需要等到接收到查詢中的所有記錄。 即使我們將獲取大小設置為4,對於1000記錄表,我們仍然最終在應用服務器的堆內存中有1000條記錄,這是正確的嗎? 獲取大小僅影響從/到數據庫服務器的往返次數。

我的問題是,如何在它到達app服務器后立即開始處理該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,則可以使用帶有游標的ResultSet,您可以一次迭代一條記錄。 您需要確保將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();
    }
}

編輯缺點(感謝@JS):

  • ojdbc之外沒有使用外部庫
  • 什么都沒有關閉
  • 拋出一個通用的異常
  • 這是一種主要方法
  • 用於生成XML的print的用法
  • Oracle特定的SQL
  • 純文本密碼
  • 有些列在字符串表示中看起來很笨拙
  • UTF-8太國際化了
  • XML結構足跡很大

在哪個階段發生OOM錯誤,它是在數據檢索或處理數據到XML文件?

如果對其進行數據檢索,則分批獲取數據。 首先獲取總行數,按主鍵對選擇進行排序,並將選定的行限制為可咀嚼的大小。

如果它在創建XML文件時,將每個客戶的XML節點發送到System.out.println,請不要將其保存在內存中。 通過commad行啟動程序並將所有輸出重定向到文件;

java MyConverter > results.txt

在循環記錄時,所有內容都保存在文件中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM