uso una tablas tabla modificar insertar eliminar datos java mysql hibernate large-data-volumes scrollableresults

java - una - Usando los resultados desplazables de Hibernate para leer lentamente 90 millones de registros



insertar,modificar y eliminar datos de una tabla en netbeans (12)

Con 90 millones de registros, parece que deberías combinar tus SELECT. Ya terminé con Oracle al hacer la carga inicial en un caché distribuido. Mirando la documentación de MySQL, el equivalente parece estar usando la cláusula LIMIT: http://dev.mysql.com/doc/refman/5.0/en/select.html

Aquí hay un ejemplo:

SELECT * from Person LIMIT 200, 100

Esto devolvería las filas 201 a 300 de la tabla Person .

Debería obtener primero el recuento de registros de su tabla y luego dividirlo por el tamaño de su lote y calcular los parámetros de bucle y LIMIT desde allí.

El otro beneficio de esto sería el paralelismo: puede ejecutar varios hilos en paralelo para un procesamiento más rápido.

Procesar 90 millones de registros tampoco suena como el punto ideal para usar Hibernate.

Simplemente necesito leer cada fila de una tabla en mi base de datos MySQL usando Hibernate y escribir un archivo basado en ella. Pero hay 90 millones de filas y son bastante grandes. Por lo tanto, parecía que lo siguiente sería apropiado:

ScrollableResults results = session.createQuery("SELECT person FROM Person person") .setReadOnly(true).setCacheable(false).scroll(ScrollMode.FORWARD_ONLY); while (results.next()) storeInFile(results.get()[0]);

El problema es que lo anterior tratará de cargar todos los 90 millones de filas en la RAM antes de pasar al ciclo while ... y eso matará mi memoria con OutOfMemoryError: excepciones de espacio de montón de Java :(.

¿Entonces supongo que ScrollableResults no es lo que estaba buscando? ¿Cuál es la forma correcta de manejar esto? No me importa si este ciclo toma días (bueno, me encantaría que no).

Supongo que la única otra forma de manejar esto es usar setFirstResult y setMaxResults para iterar a través de los resultados y simplemente usar los resultados de Hibernate en lugar de ScrollableResults. Sin embargo, parece que será ineficiente y empezará a tomar un tiempo ridículamente largo cuando llamo a setFirstResult en la fila 89 millonésima ...

ACTUALIZACIÓN: setFirstResult / setMaxResults no funciona, resulta tomar un tiempo inusualmente largo para llegar a las compensaciones como temía. ¡Debe haber una solución aquí! ¿No es este un procedimiento bastante estándar? Estoy dispuesto a renunciar a Hibernate y usar JDBC o lo que sea necesario.

ACTUALIZACIÓN 2: la solución que he encontrado que funciona bien, no es genial, es básicamente de la forma:

select * from person where id > <offset> and <other_conditions> limit 1

Como tengo otras condiciones, incluso todas en un índice, todavía no es tan rápido como me gustaría ... así que todavía estoy abierto para otras sugerencias.


De hecho, podrías haber obtenido lo que querías: resultados de desplazamiento de baja memoria con MySQL, si hubieras utilizado la respuesta mencionada aquí:

Transmisión de grandes conjuntos de resultados con MySQL

Tenga en cuenta que tendrá problemas con la carga diferida de Hibernate porque generará una excepción en cualquier consulta que se realice antes de que finalice el desplazamiento.


Debería poder usar ScrollableResults , aunque requiere algunos conjuros mágicos para trabajar con MySQL. Escribí mis hallazgos en una publicación de blog ( http://www.numerati.com/2012/06/26/reading-large-result-sets-with-hibernate-and-mysql/ ) pero resumiré aquí:

"La documentación de [JDBC] dice:

To enable this functionality, create a Statement instance in the following manner: stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE);

Esto se puede hacer utilizando la interfaz de Consulta (esto también debería funcionar para Criteria) en la versión 3.2+ de la API de Hibernate:

Query query = session.createQuery(query); query.setReadOnly(true); // MIN_VALUE gives hint to JDBC driver to stream results query.setFetchSize(Integer.MIN_VALUE); ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY); // iterate over results while (results.next()) { Object row = results.get(); // process row then release reference // you may need to evict() as well } results.close();

Esto le permite transmitir el conjunto de resultados, sin embargo, Hibernate aún guardará los resultados en la Session , por lo que deberá llamar a session.evict() o session.clear() vez en cuando. Si solo está leyendo datos, podría considerar usar una StatelessSession , aunque debería leer su documentación de antemano ".


El problema podría ser que Hibernate guarde referencias a todos los objetos en la sesión hasta que cierre la sesión. Eso no tiene nada que ver con el caché de consultas. Tal vez ayudaría a desalojar () los objetos de la sesión, una vez que haya terminado de escribir el objeto en el archivo. Si ya no son referencias de la sesión, el recolector de basura puede liberar la memoria y ya no se quedará sin memoria.


Establezca el tamaño de búsqueda en la consulta en un valor óptimo como se indica a continuación.

Además, cuando no se requiere el almacenamiento en caché, puede ser mejor utilizar StatelessSession.

ScrollableResults results = session.createQuery("SELECT person FROM Person person") .setReadOnly(true) .setFetchSize( 1000 ) // <<--- !!!! .setCacheable(false).scroll(ScrollMode.FORWARD_ONLY)



Hace poco trabajé en un problema como este y escribí un blog sobre cómo enfrentar ese problema. es muy parecido, espero ser útil para cualquiera. utilizo el enfoque de lista diferida con la adquisición parcial. Reemplacé el límite y el desplazamiento o la paginación de la consulta a una paginación manual. En mi ejemplo, el select devuelve 10 millones de registros, los obtengo y los inserto en una "tabla temporal":

create or replace function load_records () returns VOID as $$ BEGIN drop sequence if exists temp_seq; create temp sequence temp_seq; insert into tmp_table SELECT linea.* FROM ( select nextval(''temp_seq'') as ROWNUM,* from table1 t1 join table2 t2 on (t2.fieldpk = t1.fieldpk) join table3 t3 on (t3.fieldpk = t2.fieldpk) ) linea; END; $$ language plpgsql;

después de eso, puedo paginar sin contar cada fila pero usando la secuencia asignada:

select * from tmp_table where counterrow >= 9000000 and counterrow <= 9025000

Desde la perspectiva de Java, implementé esta paginación a través de una adquisición parcial con una lista perezosa. esto es, una lista que se extiende desde la lista Resumen e implementa el método get (). El método get puede usar una interfaz de acceso a datos para continuar obteniendo el siguiente conjunto de datos y liberar el montón de memoria:

@Override public E get(int index) { if (bufferParcial.size() <= (index - lastIndexRoulette)) { lastIndexRoulette = index; bufferParcial.removeAll(bufferParcial); bufferParcial = new ArrayList<E>(); bufferParcial.addAll(daoInterface.getBufferParcial()); if (bufferParcial.isEmpty()) { return null; } } return bufferParcial.get(index - lastIndexRoulette);<br> }

por otro lado, la interfaz de acceso a datos utiliza la consulta para paginar e implementa un método para iterar de forma progresiva, cada 25000 registros para completarlo todo.

los resultados de este enfoque se pueden ver aquí http://www.arquitecturaysoftware.co/2013/10/laboratorio-1-iterar-millones-de.html



Para mí funcionó correctamente al establecer useCursors = true, de lo contrario, el conjunto de resultados desplazable ignora todas las implementaciones de tamaño de búsqueda, en mi caso era 5000 pero el grupo de resultados desplazable obtuvo millones de registros a la vez causando un uso excesivo de memoria. el DB subyacente es MSSQLServer.

jdbc: jtds: sqlserver: // localhost: 1433 / ACS; TDS = 8.0; useCursors = true


Propongo más que un código de muestra , pero una plantilla de consulta basada en Hibernate para hacer esta solución para usted ( pagination , scrolling y clearing sesión de Hibernate).

También se puede adaptar fácilmente para usar un EntityManager .


Usar setFirstResult y setMaxResults es tu única opción de la que soy consciente.

Tradicionalmente, un conjunto de resultados desplazable solo transferiría filas al cliente según sea necesario. Desafortunadamente, MySQL Connector / J realmente lo falsifica, ejecuta toda la consulta y lo transporta al cliente, por lo que el controlador realmente tiene todo el conjunto de resultados cargado en la RAM y se lo alimentará por goteo (evidenciado por los problemas de falta de memoria) . Tuviste la idea correcta, solo deficiencias en el controlador java de MySQL.

No encontré ninguna forma de evitar esto, así que fui con la carga de grandes fragmentos utilizando los métodos regulares setFirst / max. Perdón por ser el portador de malas noticias.

Solo asegúrate de usar una sesión sin estado para que no haya caché a nivel de sesión o seguimiento sucio, etc.

EDITAR:

Tu ACTUALIZACIÓN 2 es lo mejor que obtendrás a menos que salgas del MySQL J / Connector. Aunque no hay ninguna razón por la que no puedas subir el límite de la consulta. Siempre que tengas suficiente memoria RAM para mantener el índice, esta debería ser una operación algo barata. Lo modificaría ligeramente, tomaría un lote a la vez y usaría la identificación más alta de ese lote para tomar el siguiente lote.

Nota: esto solo funcionará si otras condiciones usan igualdad (no se permiten condiciones de rango) y tienen la última columna del índice como id .

select * from person where id > <max_id_of_last_batch> and <other_conditions> order by id asc limit <batch_size>


Utilicé la funcionalidad de desplazamiento de Hibernate con éxito antes sin leer todo el conjunto de resultados. Alguien dijo que MySQL no hace verdaderos cursores de desplazamiento, pero afirma que está basado en el JDBC dmd.supportsResultSetType (ResultSet.TYPE_SCROLL_INSENSITIVE) y lo busca a su alrededor. parece que otras personas lo han usado. Asegúrese de que no está almacenando en caché los objetos Persona en la sesión; lo he usado en consultas SQL donde no había ninguna entidad para almacenar en caché. Puede llamar al desalojo al final del ciclo para asegurarse o probar con una consulta sql. También juegue con setFetchSize para optimizar el número de viajes al servidor.