Este artículo describe el método de procesamiento por lotes de hibernación de datos masivos. Compártelo para su referencia, como sigue:
El procesamiento de lotes de hibernación cantidades masivas en realidad es indeseable desde la perspectiva del rendimiento y se desperdicia mucha memoria. Desde su mecanismo, Hibernate primero verifica los datos que cumplen con las condiciones, los coloca en la memoria y luego realiza operaciones. El rendimiento es muy insatisfactorio en el uso real. En mi uso real, los datos de la siguiente tercera solución de optimización son: se insertan 100,000 datos en la base de datos, que demora aproximadamente 30 minutos. Jaja, débil. (Inserté 1000,000 datos en 10 minutos (los campos son relativamente pequeños))
Hay tres formas de lidiar con él para resolver problemas de rendimiento:
1: omitir la API de hibernación y use directamente la API JDBC. Este método tiene un mejor rendimiento. También es el más rápido.
2: Use procedimientos almacenados.
3: Use la API de Hibernate para realizar un procesamiento de lotes regular. Puede haber cambios, y el cambio cambiará. Cuando encontramos una cierta cantidad, podemos eliminar los datos a tiempo después de completar la operación, session.flush (); session.evict (conjunto de objeto xx); Esto también puede ahorrar algunas pérdidas de rendimiento. Esta "cierta cantidad" debe usarse como referencia cuantitativa basada en condiciones reales. Generalmente alrededor de 30-60, pero el efecto aún no es ideal.
1: Evite la API de Hibernate y hágalo directamente a través de la API JDBC. Este método tiene un mejor rendimiento y el más rápido. (El ejemplo es una operación de actualización)
Transacción tx = session.beGinTransaction (); // Tenga en cuenta que está utilizando la conexión de límite de transacción Hibernate conn = session.connection (); Preparado STMT = Conn.PreparedStatement ("Actualice el cliente como C set C.Sarlary = C.Sarlary+1 donde C.Sarlary> 1000"); stmt.exCuteUpdate (); tx.commit (); // Tenga en cuenta que está utilizando el límite de transacciones Hibernate En este applet, utiliza la API que llama directamente a JDBC para acceder a la base de datos, que es muy eficiente. Evite los problemas de rendimiento causados por la consulta y carga de primera consulta de hibernación en la memoria, y luego realizando operaciones.
2: Use procedimientos almacenados. Sin embargo, no se recomienda utilizar este método debido a la conveniencia de la portabilidad y la implementación del programa. (El ejemplo es una operación de actualización)
Si la base de datos subyacente (como Oracle) admite procedimientos almacenados, las actualizaciones por lotes también se pueden realizar a través de procedimientos almacenados. Los procedimientos almacenados se ejecutan directamente en la base de datos, más rápido. En la base de datos de Oracle, se puede definir un procedimiento almacenado llamado BatchUpDateCustomer (), el código es el siguiente:
La copia del código es la siguiente: cree o reemplace el procedimiento BatchUpDatecustomer (p_age en número) como comenzar los clientes de actualización establecida Age = Age+1 Where Age> P_age; End;
El procedimiento almacenado anterior tiene un parámetro P_AGE, que representa la edad del cliente. La aplicación puede llamar al procedimiento almacenado de las siguientes maneras:
tx = session.beGinTransaction (); conexión con = session.connection (); string procedimiento = "{call batchupDateCustomer (?)}"; CallableStatement cstmt = con.prepareCall (procedimiento); cstmt.setInt (1, 0); // Establecer el parámetro de edad en 0cstmt.executeUpdate (); tx.commit ();Como se puede ver en el programa anterior, la aplicación también debe omitir la API de Hibernate y llamar directamente a los procedimientos almacenados a través de la API JDBC.
3: Use la API de Hibernate para realizar un procesamiento de lotes regular. Puede haber cambios, y el cambio cambiará. Cuando encontramos una cierta cantidad, podemos eliminar los datos a tiempo después de completar la operación, session.flush (); session.evict (conjunto de objeto xx); Esto también puede ahorrar algunas pérdidas de rendimiento. Esta "cierta cantidad" debe ser una referencia cuantitativa basada en condiciones reales ...
(El ejemplo es una operación de guardado)
La lógica de negocios es: queremos insertar 10 0000 piezas de datos en la base de datos
tx = session.beGinTransaction (); for (int i = 0; i <100000; i ++) {Customer Custom = New Customer (); Custom.SetName ("Usuario"+I); Session.Save (Custom); if (i%50 == 0) // Use cada 50 datos como un unidades de procesamiento, que es lo que mencioné anteriormente, esta cantidad debe considerarse como apropiada {Session.flush (); Session.Esto mantendrá el sistema en un rango estable ...
Durante el proceso de desarrollo del proyecto, debido a los requisitos del proyecto, a menudo necesitamos insertar grandes cantidades de datos en la base de datos. Hay decenas de miles, decenas de miles, decenas de millones, incluso decenas de millones de ellos. Si usa Hibernate para insertar datos de este nivel de magnitud, puede ocurrir una excepción. La excepción común es OffMemoryError (excepción de desbordamiento de memoria).
Primero, revisemos brevemente el mecanismo de la operación de inserción hibernada. Hibernate necesita mantener su caché interno. Cuando realizamos la operación de inserción, pondremos todos los objetos para operar en nuestro caché interno para la administración.
Cuando se trata de caché de Hibernate, Hibernate tiene teorías de caché interno y caché secundario. Dado que Hibernate tiene diferentes mecanismos de gestión para estos dos cachés, podemos configurar su tamaño en relación con el caché secundario, mientras que para los cachés internos, Hibernate adopta una actitud de "transmisión de correa", y no hay límite en su capacidad. Ahora se encuentra el quid del problema. Cuando insertamos datos masivos, se incluirán muchos objetos en la memoria caché interna (el caché interno se almacena en caché en la memoria), de modo que la memoria de su sistema se devuelve poco a poco. Si el sistema finalmente está "frito", es razonable.
Pensemos en cómo lidiar mejor con este problema. Algunas condiciones de desarrollo deben manejarse utilizando Hibernate y, por supuesto, algunos proyectos son más flexibles y puede encontrar otros métodos.
Aquí recomiendo dos métodos:
(1): Optimice la hibernación y use el método de inserción segmentada para borrar el caché a tiempo en el programa.
(2): omite la API de hibernación y realice la inserción por lotes directamente a través de la API JDBC. Este método tiene el mejor rendimiento y el más rápido.
Para el método 1 anterior, la idea básica es: optimizar Hibernate, establecer el parámetro hibernate.jdbc.batch_size en el archivo de configuración para especificar el número de SQL enviado cada vez; El programa utiliza el método para borrar el caché en el tiempo en la inserción segmentada (la sesión implementa la escritura asíncrona, lo que permite que Hibernate escriba las operaciones explícitamente), es decir, borrarlas del caché interno a tiempo después de insertar una cierta cantidad de datos, y libera la memoria ocupada.
Para establecer el parámetro hibernate.jdbc.batch_size, puede consultar la siguiente configuración.
<Hibernate-Configuration> <Session-Factory>… <Property name = "Hibernate.jdbc.batch_size"> 50 </property> ... <Session-Factory> <Hibernate-Configuration>
La razón para configurar el parámetro hibernate.jdbc.batch_size es leer la base de datos lo menos posible. Cuanto mayor sea el valor del parámetro hibernate.jdbc.batch_size, menos veces las veces que lee la base de datos y más rápida es la velocidad. Desde la configuración anterior, se puede ver que Hibernate espera hasta que el programa se acumula 50 SQL antes de enviarlo en lotes.
El autor también está pensando que el valor del parámetro hibernate.jdbc.batch_size puede no establecerse lo más grande posible, y queda por discutir desde una perspectiva de rendimiento. Esto requiere la consideración de la situación real y establecerla según corresponda. En general, establecer 30 o 50 puede satisfacer las necesidades.
En términos de implementación del programa, el autor toma la inserción de 10,000 datos como ejemplo,
Sesión session = hibernateUtil.CurrentSession (); transatcion tx = session.beGinTransaction (); for (int i = 0; i <10000; i ++) {Student st = new Student (); St.SetName ("Feifei"); Session.Save (ST); If (i%50 == 0) // Use cada 50 datos como un procesamiento de unidades de procesamiento (session (););) // Mantenga síncrono con datos de base de datos session.clear (); // Borrar todos los datos almacenados internamente y liberar la memoria ocupada en el tiempo}} tx.commit (); ...Bajo una determinada escala de datos, este enfoque puede mantener los recursos de memoria del sistema en un rango relativamente estable.
Nota: El caché de segundo nivel mencionado anteriormente es necesario para que lo mencione aquí. Si el caché secundario está habilitado, para mantener el caché secundario, Hibernate cargará los datos correspondientes al caché secundario cuando insertamos, actualizamos y eliminemos las operaciones. Habrá una gran pérdida en el rendimiento, por lo que el autor recomienda deshabilitar el caché de nivel 2 en el procesamiento por lotes.
Para el método 2, se utiliza el procesamiento de lotes JDBC tradicional y la API JDBC se utiliza para procesarlo.
Consulte el procesamiento por lotes de Java y la autoexecución SQL.
Mirando el código anterior, ¿siempre sientes que algo es inapropiado? ¡Sí, no lo notaste! Esta sigue siendo la programación tradicional de JDBC, sin un sabor hibernado.
El código anterior se puede modificar a lo siguiente:
Transacción tx = session.beGinTransaction (); // use la conexión de procesamiento de transacciones Hibernate conn = session.connection (); PrepareStatement stmt = conn.prepareStatement ("Insertar en valores t_student (nombre) (?)"); para (int j = 0; j ++; j <200) {for (int i = 0; i ++; j <50) {stmt.setstring (1, "feifei");}} stmt.executeupdate (); tx.commit (); // Use el límite de procesamiento de transacciones hibernado ...Este cambio tendrá un sabor hibernado. Después de las pruebas, el autor utiliza la API JDBC para el procesamiento por lotes, que es casi 10 veces mayor en rendimiento que usar la API Hibernate. Este es, sin duda, el rendimiento dominante de JDBC.
En la actualización de lotes y la eliminación de Hibernate2, para las operaciones de actualización de lotes, Hibernate descubre los datos que cumplen con los requisitos y luego realiza la operación de actualización. Lo mismo es cierto para la eliminación por lotes. Primero descubra los datos que cumplan con las condiciones y luego realice la operación de eliminación.
Esto tiene dos desventajas principales:
(1): toma mucha memoria.
(2): Al procesar datos masivos, la ejecución de la instrucción Update/Eliminar es una cantidad masiva, y una instrucción Update/Delete solo puede operar un objeto. Es concebible que el rendimiento de la base de datos sea bajo si se opera con frecuencia.
Después de que se lanzó Hibernate3, se introdujo la actualización/eliminación a granel para las operaciones de actualización/eliminación de lotes. El principio es completar las operaciones de actualización/eliminación de lotes a través de una instrucción HQL, que es muy similar a las operaciones de actualización/eliminación de lotes de JDBC. En términos de rendimiento, existe una gran mejora sobre las actualizaciones/eliminación de lotes de Hibernate2.
Transacción tx = session.beginsession (); string hql = "eliminar alumno"; consulta consulta = session.createQuery (hql); int size = query.executeUpdate (); tx.commit (); ... ...
La consola emite solo una instrucción Eliminar Hibernate: Eliminar de T_Student. La ejecución de la declaración es menor, y el rendimiento es casi el mismo que usar JDBC. Es una buena manera de mejorar el rendimiento. Por supuesto, para tener un mejor rendimiento, el autor recomienda que las actualizaciones de lotes y las operaciones de eliminación aún usen JDBC. Los métodos y los puntos de conocimiento básico son básicamente los mismos que el método de inserción de lotes de lotes anterior, por lo que no lo describiré de manera redundante aquí.
Aquí proporciono otro método, que es considerar mejorar el rendimiento desde el lado de la base de datos y llamar a los procedimientos almacenados en el lado del programa Hibernate. Los procedimientos almacenados se ejecutan en el lado de la base de datos, más rápido. Tomando actualizaciones por lotes como ejemplo, se da el código de referencia.
Primero, cree un procedimiento almacenado llamado BatchUpDateStudent en el lado de la base de datos:
cree o reemplace producir lotes lotes de lote (a en número) asbeginupdate Estudiante establecido Edad = edad+1 donde edad> a; final;
El código de llamada es el siguiente:
Transacción tx = session.beginsession (); conexión conn = session.connection (); string pd = "... {call batchupDateStudent (?)}"; CallableStatement cstmt = conn.prepareCall (pd); cstmt.setInt (1, 20); // establecer el parámetro de edad en 20tx.commit ();Observando el código anterior, también omite la API de Hibernate y usa la API JDBC para llamar a los procedimientos almacenados, y utiliza los límites de la transacción de Hibernate. Los procedimientos almacenados son, sin duda, una buena manera de mejorar el rendimiento del procesamiento por lotes. Se ejecutan directamente con el lado de la base de datos y, hasta cierto punto, transfieren la presión del procesamiento por lotes a la base de datos.
Posdata
Este artículo analiza las operaciones de procesamiento por lotes de Hibernate, y el punto de partida es considerar mejorar el rendimiento, y solo proporciona un pequeño aspecto para mejorar el rendimiento.
No importa qué método se adopte, debe considerarse basado en condiciones reales. Proporcionar a los usuarios un sistema eficiente y estable que satisfaga sus necesidades es la máxima prioridad.
Espero que este artículo sea útil para la programación hibernada de todos.