Comment éviter OOM (Mémoire insuffisante) Erreur lors de la récupération tous les enregistrements de la table énorme?

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

Question

Je me donne une tâche de convertir une grande table dans un fichier XML personnalisé. Je vais utiliser Java pour ce travail.

Si je lance simplement un « SELECT * FROM client », il peut retourner énorme quantité de données qui a fini par causer OOM. Je me demande, est-il un moyen que je peux traiter immédiatement l'enregistrement une fois qu'il sera disponible, et supprimer l'enregistrement de la mémoire après que pendant le processus de récupération SQL?

--- édité le 13 juil 2009

Permettez-moi de préciser ma question. J'ai 1 serveur db et 1 serveur d'applications. Lorsque je lance une requête de sélection dans l'application, les données se déplaceront à partir du serveur db au serveur d'applications.

Je crois (corrigez-moi si je me trompe) ResultSet devra attendre de recevoir tous les enregistrements dans la requête. Même si nous fixons chercher taille 4, pour une table 1000 record, on finit toujours par avoir 1000 enregistrements en mémoire tas de serveur d'applications, est-il exact? Fetch taille seulement affecter le nombre de retour de / vers le serveur db.

Ma question est, comment commencer le traitement de ce 4 (ou tout autre nombre) enregistre immédiatement après son arrivée à l'application serveur, et d'en disposer pour libérer de la mémoire dans le serveur d'application?

Était-ce utile?

La solution

Je pense que vous pouvez utiliser la même solution que celle-ci . A scrollable resultset.

Autres conseils

I Avec un peu plus d'informations, obtenir une réponse plus utile.

Si vous utilisez MySQL:

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

de http://www.oracle.com/ technologie / technologie / 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);

Si vous utilisez JDBC, vous pouvez utiliser un ResultSet avec un curseur qui vous itérer un enregistrement à la fois. Vous devez vous fait alors que vous écrivez votre XML dans un fichier un enregistrement à la fois plutôt que d'utiliser DOM pour construire le XML.

Une règle que je l'ai appris de mon expérience est que vous apportez jamais toutes les données de la base de données sur votre serveur d'applications. Une chose que vous pouvez faire est de mettre en œuvre une procédure à la page de vos données.

Vous pouvez apporter une page de données contenant environ 1000-5000 données, les traiter, puis de nouveau les données aller chercher la page suivante.

Un concept pour l'exportation de la table entière. (Note aux experts. Je suis conscient de ses lacunes)

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();
    }
}

Modifier Les défauts (merci @ J.S.):

  • Pas de bibliothèques externes utilisées au-delà du ojdbc
  • Rien est fermé
  • Une exception générique est jeté
  • Il est une méthode principale
  • Utilisation de l'impression pour la génération XML
  • SQL spécifique Oracle
  • Mot de passe texte brut
  • Certaines colonnes semblent maladroits dans la représentation de chaîne
  • UTF-8 est trop international
  • empreinte structure XML est grand

A quel stade est l'erreur OOM se produit, est-il sur des données de récupération ou de traitement des données dans un fichier XML?

Si la récupération de données, obtenir les données par lots. Obtenez le nombre total de lignes en premier lieu, commander les sélectionne par la clé primaire et limiter les lignes sélectionnées aux tailles masticables.

Si son à la création du fichier XML, envoyer le noeud XML de chaque client à System.out.println, ne tenez pas en mémoire. Lancer le programme par l'intermédiaire de la ligne de commad et rediriger toutes les sorties dans un fichier;

java MyConverter > results.txt

Comme on boucle le record est enregistré dans le fichier.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top