style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

Como poder bajar una BD que tenga más de 100.000 registros?

Que tal comunidad, como les va? Me acerco a Uds. para saber si me pueden dar un consejo ya que necesito hacer un reporte de una base de datos que tiene más de 100.000 registros, cada registro obtiene datos de otras tablas y esto me provoca una excepción de tipo: java.lang.OutOfMemoryError: Java heap space, ya intente aumentando la memoria en el Apache TomCat, pero eso únicamente retraso el proceso.

Actualmente el aplicativo esta desarrollado con Hibernate, Spring, JSP, JSTL y JQuery. Quisiera que la herramienta tuviera un mejor rendimiento en ese proceso, por ello no sé si sea más efectivo bajar la consulta en un archivo de texto plano, o hacerlo con spring bach. Si alguien tiene otra alternativa con gusto le escucho.

Gracias y saludos.

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Es difícil dar

Es difícil dar recomendaciones por que cada caso es diferente, pero algo que a mí me ha funcionado en el pasado es:

1.- Hacer el calculo en la base de datos con un stored procedure en vez de hacer que todo viaje por la red.

2.- Cuando esto no es factible puedes hacer que cada cadena que llegue a la JVM se internalice ( es decir llamar a algunString.intern() ) esto tiene el efecto que dos cadenas que sean identicas, ocupen el mismo espacio en memoria, esto reduce dramaticamente la memoria pero aumenta el permgem

3.- Almacenar resultados parciales en una tabla temporal

Estas tres técnicas son diferentes, y a veces se pueden aplicar en conjunto, a veces no. En especial con la segunda yo he logrado bajar el uso de 2gb a 20mb de memoria, cuando se están creando demasiados string que tienen el mismo valor.

Lo más importante es quizá, que tomes un profiler y averigües donde está ocupada toda esa memoria. Desde Java1.5 el SDK viene equipado con un profiles que se llama VisualVM. Si no usas un profiler difícilmente sabrás que es lo que estas haciendo con tu memoria, así que empieza por ahí.

Chau!

Imagen de AlexSnake

Sin profiler

Qué onda Oscar gracias por compartir tus puntos. Efectivamente no estoy ocupando ningún profiler, es más comenzaré a buscar más al respecto, sin embargo me llama mucho la atención el punto número 2 que mencionas, porque realmente lo que estoy haciendo es eso bajar muchas cadenas que tienen la misma longitud (aun que no el mismo valor) y supongo que lo que tengo que aplicar es eso, aun que tengo más o menos una idea de lo que tengo que implementar hare un par de pruebas y lo publicare más adelante que obtuve.
Saludos.

Imagen de ezamudio

GC y archivo temporal

Si estas generando un reporte con esos 100K registros, puedes irlo generando (escribiendo a un archivo) conforme lees de la base de datos, y cada mil registros (deja ese numero parametrizable) invocas System.gc(). Al final devuelves en la respuesta el archivo temporal, segun el framework que uses, puedes incluso nada mas pasar una referencia a un InputStream que apunte al archivo para que de ahi se vayan leyendo datos que se van escribiendo a un OutputStream en el socket de HTTP para la respuesta.

Imagen de ezamudio

Spring

Tambien seria bueno saber como estas intentando leer los registros actualmente. Es un solo query con joins, y lo quieres leer con un JdbcTemplate de Spring que te devuelva la lista de 100K registros? Eso no va a funcionar... debes usar alguno de los metros que reciben un objeto con un metodo que es invocado por cada registro, para que ahi leas el registro y lo metas a un reporte (archivo de texto plano o lo que sea) y te olvides de esos datos. No es necesario que tengas los 100K registros en memoria.

Acá puse una respuesta más o

Acá puse una respuesta más o menos larga de la opción 2, usar intern ( me habían borrado esa respuesta, pero ya logré tenerla de vuelta - gracias a la comunidad SO - )

Revisala e intenta hacer el cambio en tu código. Te debe de tomar menos de ... 10 - 15 minutos.

Solo ten en cuenta que necesitarás más espacio un tu permgen
http://stackoverflow.com/questions/3094925/trying-to-solve-15-puzzle-out...

Acá la traducción automática:

http://goo.gl/tcL17

Imagen de AlexSnake

Garbage Collector

Hola ezamudio gracias por tu respuesta, como bien mencionas ya habia ocupado el System.gc(); aun que yo creo que no lo habia ocupado correctamente ya que el proceso se volvia mas lento y no me funcionaba de la mejor manera. Actualmente mi consulta la hago con hibernate así:

Criteria criteria = sfact.getCurrentSession().createCriteria(Clase.class);
criteria.setFetchMode("columna_1", FetchMode.JOIN);
criteria.add(Restrictions.eq("columna_1", objeto_1));
//... asi varias restricciones hasta:
return (ArrayList<Clase>) criteria.list();

Saludos

Imagen de AlexSnake

Profiler VisualVM 1.1

Oks oscar le dare una hojeada a los links que mencionas :)
Y buscando mas sobre el profiler, no me habia percatado que MyEclipse ya lo trae por defecto, y las graficas que muestran son el consumo del heap size y el used heap, se me hace una buen plug in pero debo saber como usarla o en todo caso interpretarla.

Ooohh no me habia dado cuenta que los links son del mismo VisualVM jejeje :P

Imagen de Nopalin

Pues depende como estés

Pues depende como estés buscando la información, pero como recomienda ezamudio, el error ya sabemos por que és, lo que yo haria es buscar páginas, osea resultados de 10 mil en 10 mil hasta terminar.

Además, si estás utilizando hibernate, te recomendaria que utilizaras un scalar query y que solo retornaras los valores que requiere, por que hibernate no es muy bueno optimizando que digamos, tambien checa si tus relaciones son LAZY, (yo te recomendaria poner todas LAZY) para que las busquedas sean mas rápidas.

sobres

Imagen de AlexSnake

Ando en ello

Si, gracias Nopalin ando en ello pero toda via no logro nada, espero publicar pronto la solución.
Saludos

Imagen de AlexSnake

Solución

Ok ya tengo una solución y quiero compartirla con ud, espero me den su punto de vista. Primero intente la 2da y 3ra manera en la que me indico OscarRys pero no mas no pude, después basado en el comentario de ezamudo, navegue un poco y encontré como resolverlo, este es el resultado:

        Criteria criteria = sfact.getCurrentSession().createCriteria(Clase.class);
        //aqui filtros
                       
        ScrollableResults ClaseScroll = criteria.setCacheMode(CacheMode.IGNORE).scroll(ScrollMode.FORWARD_ONLY);
                       
        fichero = new File(sFichero);
                       
        if( fichero.exists() )
                fichero.deleteOnExit();
                       
        BufferedWriter bw = new BufferedWriter(new FileWriter(sFichero));
                       
        while(ClaseScroll.next()){
                Clase = ( (Clase)ClaseScroll.get(0) );
                bw.write( Clase.getIdfol() + "\t" + Clase.getRemesa().getRemnom() );
                bw.newLine();
                               
                if (cta % 1000 == 0) {
                        sfact.getCurrentSession().flush();
                        sfact.getCurrentSession().clear();
                        System.gc();
                        bw.flush();
                }
                cta++;
        }
                       
        bw.close();

Aquí la explicación a la respuesta.

Saludos

Que bueno que ya te quedo.

Que bueno que ya te quedo. Supongo que lo del intern() no lo pudiste aplicar por que la creación del string se hace dentro del framework a donde no tenías acceso.

Por ejemplo si hubieras podido hacer esto:

   String valor = rs.getString("valor");

Podrías haber aplicado:

    String valor = rs.getString("valor").intern(); // obvio siempre y cuando no fuera null

Pero como eso está dentro, del código de Hibernate, pues difícilmente.

Felicidades por lograrlo!!

:) :)

Por cierto, cual es la

Por cierto, cual es la importante? CacheMode.IGNORE? o FORWARD_ONLY? O las dos?

Asumo, que con el CaheMode.IGNORE, ya no se queda con los datos en memoria, sino que los deshecha después de usarlos. Entiendo entonces, que tu los agregabas en tu reporte y los desechabas también. Dejando que el GC hiciera su chamba.

Es esto correcto?

Imagen de AlexSnake

Tks Oscar

Correcto no puede aplicarlo por eso y por otras mas jejeje :P pero espero en algun momento aplicarlo.
Saludos

Imagen de ezamudio

optimización

En estos casos puede ser importante aplicar algunas optimizaciones aunque parezcan insignificantes. Por ejemplo cambiar:

bw.write( Clase.getIdfol() + "\t" + Clase.getRemesa().getRemnom() );

por algo como

bw.write( Clase.getIdfol() );
bw.write('\t'); //caracter, no cadena
bw.write( Clase.getRemesa().getRemnom() );

Así evitas que se haga un StringBuilder internamente donde se concatenan las dos propiedades del objeto con un tabulador en medio. Y en cuanto al gc, puedes hacer algo así:

short contador = 0;
while (claseScroll.next()) {
  //lo demas que ya tienes
  contador++;
  if (contador == 10000) { //o 5000 o 3000 o lo que te funcione
    contador = 0;
    System.gc();
  }
}

con eso puedes ir liberando memoria durante el proceso y ya no importa si te avientas millones de registros, no se va a saturar.

Imagen de AlexSnake

En respuesta a...

cuál es la importante? Estas en lo cierto ya que debe ser con CacheMode.IGNORE puesto que anteriormente habia dejado solo el scroll(ScrollMode.FORWARD_ONLY); y seguia tronando. Pues sinceramente solo agregue el GC para reducir la Memoria sin saber si era mas optimo.

optimización Gracas ezamudio lo tomare en cuenta y lo cambiare a la brevedad.

Saludos...

Imagen de Nopalin

clear cache

En realidad quien hace el truco es:

sfact.getCurrentSession().flush();
sfact.getCurrentSession().clear();

De esta forma estas limpiando la session de hibernate y liberas memoria (los datos que trajo).

Puedes probar quitando el cache IGNORE y la llamada al gc y verás el mismo resultado.

sobres

Imagen de AlexSnake

Clear cache..

Lo que yo me percate cuando deje solo esas dos lineas que mencionas lo que obtuve fueron mas resultados pero no todos, es decir, si libera la memoria pero no al 100, fue cuando busque como obtener registros masivos con Hibernate y encontre que debia hacerlo con el CacheMode.IGNORE incluso deployee la aplicacion con el profile y realmente el consumo de memoria fue menos.

Gracias por comentar, saludos.

Unas grafiquitas de antes y

Unas grafiquitas de antes y después y esto quedaría excelente :)

Imagen de AlexSnake

Va!!

Ok aquí estan las graficas

Antes:

Uploaded with ImageShack.us

Despues:

Uploaded with ImageShack.us

Imagen de AlexSnake

Comparación:

Ya que no me quise quedar con las ganas de ver la comparación de graficas entre usar o no el CacheMode.Ignore hice nuevamente las pruebas y por lo tanto:

Corrijo y me disculpo, tienes razón Nopalin no truena y trae los mismos registros sin embargo hay una pequeña diferencia entra usar o no el CacheMode.IGNORE Aquí la diferencia en comparación:

Saludos.

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">