2009-07-10 9 views
15

Tengo la tarea de convertir una tabla enorme en un archivo XML personalizado. Usaré Java para este trabajo.Cómo evitar el error OOM (Memoria insuficiente) al recuperar todos los registros de la tabla grande?

Si simplemente emite un "SELECT * FROM customer", puede devolver una gran cantidad de datos que eventualmente causan OOM. Me pregunto, ¿hay alguna manera de procesar el registro inmediatamente una vez que esté disponible, y eliminar el registro de la memoria después de eso durante el proceso de recuperación de sql?

--- editado el 13 jul 2009

Permítanme desarrollar mi pregunta. Tengo 1 servidor de db y 1 servidor de aplicaciones. Cuando publico una consulta de selección en la aplicación, los datos se trasladan del servidor de db al servidor de aplicaciones.

Creo (corríjanme si estoy equivocado) ResultSet tendrá que esperar hasta recibir todos los registros en la consulta. Incluso si establecemos el tamaño de búsqueda como 4, para una tabla de 1000 registros, seguimos teniendo 1000 registros en la memoria del servidor de aplicaciones, ¿es correcto? El tamaño de búsqueda solo afecta el número de ida y vuelta desde/hasta el servidor de db.

Mi pregunta es, ¿cómo empezar a procesar en esos 4 (o cualquier número) registros inmediatamente después de su llegada al servidor de la aplicación, y deshacerse de él para liberar memoria en el servidor de la aplicación?

+0

Qué servidor SQL son estás usando (es decir, MS, Oracle, MySql, etc.)? Existen diferentes formas de manejar este problema dependiendo de lo que esté usando. –

+0

Muestra el código que usas para recuperar los registros. No está claro si solo iterar sobre ellos, o almacenarlos localmente en alguna colección y luego iterar sobre eso. –

+0

Voy a usar Oracle para esta tarea. Todavía no he comenzado ningún código, porque todavía estoy investigando la mejor manera de hacerlo :) – janetsmith

Respuesta

4

Creo que se podría utilizar la misma solución que this one. Un conjunto de resultados desplazable.

+1

gracias. Esta también es una posible solución, sin embargo con este método, solo 1 registro por viaje de ida y vuelta (tamaño de búsqueda = 1)? – janetsmith

6

Con un poco más de información, puedo obtener una respuesta más útil.

Si está utilizando 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/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); 
+1

Integer.MIN_VALUE? De Verdad? Ese es un número negativo. ¿No quieres algo como, 10, 100 o 1000? –

+0

Estoy usando Oracle. Su código MySQL me da una buena dirección :) – janetsmith

+0

+1 TYPE_FORWARD_ONLY es una gran sugerencia para el DB en este caso. Luego depende del controlador JDBC si hace algo útil con él. –

4

Si está utilizando JDBC, puede usar un ResultSet con un cursor que itere a través de un registro a la vez. Debe asegurarse de escribir el XML en un archivo, registro por registro, en lugar de utilizar DOM para compilar el XML.

+1

En cuanto a la generación de XML, he pensado en eso antes. Voy a usar StAX para la salida xml (http://blogger.ziesemer.com/2007/06/xml-generation-in-java.html) – janetsmith

4

Una regla general que he aprendido de mi experiencia es que NUNCA traes TODA la información de la base de datos a tu servidor de aplicaciones. Una cosa que puede hacer es implementar un procedimiento para ubicar sus datos.

Puede traer una página de datos que contenga alrededor de 1000-5000 registros, procesarlos, luego volver a buscar los datos para la página siguiente.

+1

En segundo lugar esta opción. Pero el límite de ejemplo no es bueno en este caso. Amplíelo, digamos entre 1000 y 5000. Volver a buscarlo no llevaría mucho tiempo, porque ya habría un plan de ejecución disponible. Además, funciona con todos los servidores de bases de datos. –

+0

Creo que incluso 10,000 a 50,000 o 100,000 podrían funcionar. No estoy seguro de qué tan grande es la información de una fila. Pero 50,000 parece estar bien. Pruébelo usted mismo. –

+0

@Vinagar: el límite ha sido editado. Todo depende de la cantidad de memoria disponible y cuál es el contenido de la tabla. Pero, creo que 1000-5000 es mejor. – Kirtan

0

¿En qué etapa se produce el error OOM, está en la recuperación de datos o procesando datos en un archivo XML?

Si su recuperación de datos, obtenga los datos en lotes. Obtenga primero el número total de filas, ordene las selecciones por la clave principal y limite las filas seleccionadas a tamaños masticables.

Si al crear el archivo XML, envía el nodo XML de cada cliente a System.out.println, no lo guarde en la memoria. Inicie el programa a través de la línea commad y redirija todo el resultado a un archivo;

java MyConverter > results.txt 

Mientras recorre el registro, todo se guarda en el archivo.

1

Un concepto para exportar toda la tabla. (Nota para los expertos: soy consciente de sus deficiencias.)

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

Editar Las deficiencias (gracias @JS):

  • bibliotecas externas utilizadas más allá de la ojdbc
  • Nada está cerrado
  • Una excepción genérica se lanza
  • Es un método principal
  • Uso de la impresión para XM generación L
  • Oracle SQL específica
  • contraseña en texto plano
  • Algunas columnas se ven torpe en representación de cadena
  • UTF-8 es demasiado internacional
  • XML estructura de la huella es grande
+0

Cuando publicar el código de muestra y conocer las deficiencias, entonces al menos deberías mencionarlas. Como sabe, el código * de ejemplo * se mostrará en los sistemas de producción. –

Cuestiones relacionadas