كيفية تجنب خطأ OOM (خارج الذاكرة) عند استرداد جميع السجلات من طاولة ضخمة؟

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

سؤال

أنا أعطيت مهمة لتحويل جدول ضخم إلى ملف XML مخصص. سوف أستخدم جافا لهذه الوظيفة.

إذا قمت ببساطة بإصدار "تحديد * من العميل"، فقد يرجع كمية هائلة من البيانات التي تسبب في نهاية المطاف OOM. أتساءل، هل هناك طريقة يمكنني معالجة السجل فورا بمجرد توفرها، وإزالة السجل من الذاكرة بعد ذلك أثناء عملية استرداد SQL؟

--- تحرير في 13 يوليو 2009

اسمحوا لي أن توضح سؤالي. لدي خادم DB 1 و 1 تطبيق خادم. عند إصدار استعلام محدد في التطبيق، ستسافر البيانات من خادم DB إلى خادم التطبيقات.

أعتقد (صححني إذا كنت مخطئا)، ستحتاج النتائج إلى الانتظار حتى تلقي جميع السجلات في الاستعلام. حتى لو قمت بتعيين حجم الجلب ك 4، للحصول على جدول 1000 سجل، ما زلنا في نهاية المطاف وجود 1000 سجل في ذاكرة Heap من خادم التطبيقات، هل صحيح؟ حجم جلب يؤثر فقط على عدد رحلة ذهاب وعودة من / إلى خادم DB.

سؤالي هو، وكيفية البدء في معالجة سجلات 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();
    }
}

يحرر أوجه القصور (شكراjs):

  • لا توجد مكتبات خارجية تستخدم خارج OJDBC
  • لا شيء مغلق
  • يتم طرح استثناء عام
  • إنها طريقة رئيسية
  • استخدام الطباعة لتوليد XML
  • أوراكل محددة SQL.
  • كلمة مرور نص عادي
  • بعض الأعمدة تبدو محرجة في تمثيل السلسلة
  • UTF-8 دولية جدا
  • بنية بنية XML كبيرة

في أي مرحلة هي خطأ OOM الذي يحدث، هل هو على استرجاع البيانات أو معالجة البيانات إلى ملف XML؟

إذا استرجاع البيانات، احصل على البيانات في دفعات. احصل على إجمالي عدد الصفوف أولا، وطلب التحديد عن طريق المفتاح الأساسي والحد من الصفوف المحددة إلى الأحجام القابلة للمضغ.

إذا كانت في إنشاء ملف XML، فقم بإرسال عقدة XML لكل عميل إلى system.out.println، لا تحتفظ بها في الذاكرة. قم بتشغيل البرنامج عبر خط Commad وإعادة توجيه جميع الإخراج إلى ملف؛

java MyConverter > results.txt

كما أنت حلقة من خلال السجل يتم حفظ الكل في الملف.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top