2009-11-24 9 views
9

estoy usando Hibernate EntityManager, y estoy experimentando una desaceleración raro en mis consultas Hibernate. Eche un vistazo a este código:consultas Hibernate ralentizar drásticamente después de una entidad se carga en la sesión

public void testQuerySpeed() { 
    for(int i = 0; i < 1000; i++) { 
     em.createNativeQuery("SELECT 1").getResultList(); 
    } 
} 

Esto se ejecuta en aproximadamente 750ms en mi máquina. No es increíblemente rápido teniendo en cuenta que solo selecciona un entero constante, pero aceptable. Mi problema surge el momento ninguna entidad se cargan en mi sesión de EntityManager antes de lanzar mi consulta:

public void testQuerySpeed() { 
    CommercialContact contact = em.find(CommercialContact.class, 1890871l); 

    for(int i = 0; i < 1000; i++) { 
     em.createNativeQuery("SELECT 1").getSingleResult(); 
    } 
} 

El em.find() es rápido, pero el tiempo de ejecución 1000 consultas aumentó más de diez veces, hasta aproximadamente 10 segundos. Si pongo un em.clear() después del em.find(), el problema desaparece nuevamente y el tiempo de ejecución vuelve a 750ms.

he utilizado una consulta nativa aquí, pero existe el problema con HQL consulta también. Parece que TODAS las consultas toman al menos 70 ms cada vez que una entidad se encuentra en la sesión de EntityManager.

Esta caída de rendimiento realmente nos está haciendo daño al generar listas en las que se necesitan n + 1 consultas.

He probado la última Hibernate 3.5 beta, y tienen exactamente el mismo problema. ¿Alguien ha visto este problema o alguna idea sobre cómo solucionarlo?

estoy usando PostgreSQL 8.3, utilizando las transacciones locales de recursos (que se ejecuta en Tomcat). Usar el grupo de conexiones integrado, pero usar C3P0 no hizo diferencia.

+0

¿Se perfila? ¿Dónde se gasta la mayor parte del tiempo? ¿Tiene algún oyente/interceptores/métodos de devolución de llamada definidos? – ChssPly76

Respuesta

10

También tengo que recomendar el uso de un perfil de JVM para ver a dónde va el tiempo. También puede que no le importe activar el registro de la declaración SQL para la sesión de Hibernate solo para asegurarse de que no esté ejecutando más SQL de lo que cree que es.

La primera cosa que viene a la mente es el comportamiento de "lavado" de la sesión de Hibernate. ¿Estableces explícitamente un modo de descarga específico en la sesión? De lo contrario, obtendrá un lavado "automático" que hará una cierta cantidad de comprobación de los objetos que tiene en su sesión para determinar si hay o no cambios en la memoria que deban "volverse" a la base de datos (dentro de una transacción) , por supuesto).

Creo que lo más fácil de tratar primero para ver si tiene algún efecto es modificar el código de prueba que mostró anteriormente para especificar que desea rubor que se produzca de forma manual cuando confirme su transacción de base de datos:

public void testQuerySpeed() { 
    em.setFlushMode(FlushModeType.COMMIT); // assuming you're using JPA annotations 
    CommercialContact contact = em.find(CommercialContact.class, 1890871l); 

    for(int i = 0; i < 1000; i++) { 
     em.createNativeQuery("SELECT 1").getSingleResult(); 
    } 
} 

Otro pensamiento que supongo sería preguntar si puede o no realizar sus tareas masivas en un EntityManager separado, lo que podría funcionar si solo está haciendo UPDATE o INSERT.

+1

De hecho, el modo de descarga se estableció en AUTO en mi código. He configurado el modo de descarga para COMPROMETER en nuestro código de informe, no hacemos ninguna creación de entidad allí de ninguna manera. Esto efectivamente soluciona el problema de rendimiento. Es extraño que el control de objetos sucios requiera alrededor de 60 ms para una sola entidad. ¡Muchas gracias por su comprensión! –

+0

¡OMG, muchas gracias, Alexander y BryandD! ¡Ambos salvaste mi vida! :) Tuve exactamente el mismo problema aquí, con una transacción que contiene un bucle con varias búsquedas e inserciones/actualizaciones en el interior. Después de una semana cambiando algoritmos, búsqueda de Google y creación de perfiles, mi problema de rendimiento se resolvió con un simple: em.setFlushMode (FlushModeType.COMMIT); Antes de eso, todo el proceso tomó aproximadamente 28 minutos, el 95% se gastó en getResultList(). Después, todo va bien con solo 2 minutos. Me pregunto por qué jpa/hibernate no elige FlushModeType.COMMIT el FlushMode predeterminado para EntityManager ... – hbobenicio

1

Está ejecutando una consulta nativa que no dice nada sobre lo que tocará y, por lo tanto, Hibernate tendrá que (por coherencia) flush() contra todos los datos de todas las tablas que conoce (y su single find() podría haber obtenido más de un objeto, por lo que podría no ser una operación trivial).

Para optimizar esto, asegúrese de que utiliza los métodos SQLQuery.add * para definir lo que la consulta está haciendo realidad. En este caso, query.addSynchronizedQuerySpace ("bogustablename") debería hacer el truco de decirle a Hibernate que esta consulta es solo información escalar de ninguna tabla específica.

3

Tenía esencialmente el mismo problema (consulta dentro de un bucle). Procedí a una verificación de perfil con JProfiler ...la ejecución de mi método de interés pasó 572 segundos e hibernar la verificación sucia toma 457 segundos de esta vez (aproximadamente 80%). Asombroso, ¿verdad? Debo decir que tenía muchas entidades administradas por EntityManager. Si presento em.flush()/em.clear() o em.setFlushMode (FlushModeType.COMMIT) en el código ofensivo, el problema de rendimiento desaparece.

resultado de perfiles está disponible en http://img1.imagilive.com/0110/hibernate_dirty_checking_bad_perfomances0ce.png

Cuestiones relacionadas