2010-05-04 15 views
9

Acabamos de finalizar el perfil de nuestra aplicación. (ella comienza a ser lenta). el problema parece ser "in hibernate".Problema habitual de rendimiento de hibernación

Es un mapeo heredado. Quién trabaja, y hazlo es trabajo. El shema relacional detrás está bien también.

Pero algunas solicitudes son lentas como el infierno.

Por lo tanto, apreciaríamos cualquier comentario sobre el error común y habitual cometido con la hibernación que termina con una respuesta lenta.

Ejemplo: Eager en lugar de Lazy puede cambiar el tiempo de respuesta dramaticly ....


Edit: Como de costumbre, leer el manual es a menudo una buena idea. Una cubierta capítulo entero este tema aquí:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html

Respuesta

17

Una de las trampas más comunes es la infame n+1 selects problem. Por defecto, Hibernate no carga datos que no solicitó. Esto reduce el consumo de memoria pero lo expone al problema n + 1 que se puede evitar al cambiar a la estrategia de búsqueda correcta para recuperar todos los datos que necesita para cargar objetos en su estado inicializado apropiadamente.

Pero también no se ha podido ir demasiado o se encontrará con el problema contrario, el problema del producto cartesiano : en lugar de ejecutar a muchos comandos SQL, puede acabar creando declaraciones que recuperan demasiados datos.

Ese es el objetivo de la sintonización: encontrar el medio entre los datos insuficientes y demasiados para cada caso de uso de la aplicación (o al menos, para el que se necesita ajustar).

Mis recomendaciones:

  • primer registro de activación de SQL a nivel de Hibernate
  • corren los casos de uso crítico (el 20% utiliza el 80% de las veces) o incluso la totalidad de ellos si tiene ese lujo
  • identificar las consultas sospechosas y optimizar el plan de buscar, comprobar si los índices se utilizan adecuadamente, etc
  • implican su DBA
+0

Aceptado para la introducción del problema de selección N + 1. –

+0

La alternativa n + 1 frente a cartesiana (generalmente a través de "uniones de monstruo") es (todavía) un problema común de ORM inmaduro (incluso de los principales editores de software). Afortunadamente, (N) Hibernate tiene el procesamiento por lotes de consultas como un buen intermediario; agrupará las consultas y recuperará objetos secundarios por listas de ID o subselección, si se carga de forma lenta o por el gráfico completo del objeto. Por lo tanto, el usuario obtendrá quizás una docena de consultas en lugar de cientos por n + 1 o un monstruo unido con gigabytes de datos de resultados. –

0

Una cosa que sucedió en mi empresa viene a la mente. Se puede ver si la carga de una entidad también carga alguna entidad serialized, que se deserializará cada vez que se cargue la entidad. Además, al comprometer la transacción hibernate podría hacer un flush() por usted (es configurable). Si se vacía, para mantener la persistencia, hará una comparación de la entidad comprometida y la de la base de datos. En este caso, hará una comparación en el objeto serialized, que lleva mucho tiempo.

Otra cosa que podría hacer es comprobar si tiene una persistencia innecesaria en cascada, es decir, anotación @Cascade({CascadeType.PERSIST, CascadeType.SAVE_UPDATE}) en las columnas.

Otra cosa que podría hacer, que es no específicamente relacionado con hibernación, es que se crea view s de hacer una sola consulta, en lugar de hacer montones y montones de consultas a diferentes tablas. Eso hizo una gran diferencia para nosotros en una determinada función.

0

Como ya mencionamos, el rendimiento de Hibernate es la configuración correcta. Una vez pude mejorar la velocidad de actualización de caché de credenciales en un factor de 10 (de 2 a 200 ms) al cambiar a una sesión sin estado (ese código en particular no dependía de ningún tipo de recuperación lenta para poder hacer lo que hice) .

2

Me gustaría respaldar todo lo que dijo Pascal, y solo mencionar que una solución al "problema de recuperación de demasiados datos" es usar proyecciones de Hibernate, y buscar los datos en un DTO plano, que solo consiste en de las columnas realmente necesarias. Esta es específicamente una opción, cuando no está planeando actualizar los datos en esta sesión de Hibernate, por lo tanto, no necesitará objetos mapeados. Tomado de this article, que contiene más consejos de rendimiento de Hibernate.

0

El mapeo de las relaciones m: n y n: 1 son la raíz de los frecuentes problemas de rendimiento con Hibernate.

Hibernar El almacenamiento en caché no puede ayudar mucho si utiliza HQL ya que Hibernate no puede traducir consultas en llamadas a caché, por lo que no puede usar caché con HQL (al menos cuando estaba leyendo su código).

Si desea método simple, rápido y ligero, puedo recomendar fjorm

Dislaimer: Soy uno de los fundadores del proyecto fjorm

2

Los errores más comunes son los N + 1 le permite elegir que se esconden detrás de las escenas. Por lo general, esos no se detectan antes de la producción y los perfiladores habituales no son excelentes para revelarlos.

Usted puede tratar de un perfilador interactivo como XRebel - que funciona todo el tiempo y revela problemas durante el desarrollo, para que puedas corregirlos de inmediato y formar a sus desarrolladores para no cometer los mismos errores.

http://zeroturnaround.com/blog/hibernate-orm-with-xrebel-revealing-multi-query-issues-with-an-interactive-profiler/

XRebel showing SQL queries

0

como ya se ha mencionado la N+1 problem es el tema común en las aplicaciones de la APP. Actualmente estoy trabajando en una herramienta para la detección temprana de estos problemas en las pruebas de su unidad y en sus entornos de prueba.Se llama JDBC Sniffer, que es de código abierto y completamente libre

que le permite controlar el número de consultas ejecutadas en sus entornos de prueba directamente en su navegador: enter image description here

También proporciona extensino a los marcos de prueba Unidad Popular para detectar el problema de N + 1 mientras desarrolla el código mediante anotaciones:

import com.github.bedrin.jdbc.sniffer.BaseTest; 
import com.github.bedrin.jdbc.sniffer.Threads; 
import com.github.bedrin.jdbc.sniffer.Expectation; 
import com.github.bedrin.jdbc.sniffer.Expectations; 
import com.github.bedrin.jdbc.sniffer.junit.QueryCounter; 
import org.junit.Rule; 
import org.junit.Test; 

public class QueryCounterTest extends BaseTest { 

    // Integrate JDBC Sniffer to your test using @Rule annotation and a QueryCounter field 
    @Rule 
    public final QueryCounter queryCounter = new QueryCounter(); 

    @Test 
    @Expectations({ 
      @Expectation(atMost = 1, threads = Threads.CURRENT), 
      @Expectation(atMost = 1, threads = Threads.OTHERS), 
    }) 
    public void testExpectations() { 
     executeStatement(); 
     executeStatementInOtherThread(); 
    } 

} 
Cuestiones relacionadas