2009-10-05 4 views
13

Tengo un sitio web con mucho tráfico y uso hibernate. También uso ehcache para almacenar en caché algunas entidades y consultas que son necesarias para generar las páginas.Evitar múltiples repoblaciones de la misma región de caché (debido a la concurrencia)

El problema es "caché paralela falla" y la explicación larga es que cuando la aplicación se inicia y las regiones de caché están frías, cada región de caché se llena muchas veces (en lugar de solo una) por diferentes subprocesos porque el sitio está golpeado por muchos usuarios al mismo tiempo. Además, cuando alguna región de caché invalida, se está repoblando muchas veces por el mismo motivo. ¿Cómo puedo evitar esto?

Logré convert 1 entity and 1 query cache to a BlockingCache proporcionando mi propia implementación a hibernate.cache.provider_class, pero la semántica de BlockingCache no parece funcionar. Lo que es peor, a veces bloquea los bloqueos de BlockingCache (bloques) y la aplicación se cuelga por completo. El volcado de subprocesos muestra que el procesamiento está bloqueado en el mutex de BlockingCache en una operación de obtención.

Entonces, la pregunta es, ¿Hibernate admite este tipo de uso?

Y si no, ¿cómo resuelves este problema en la producción?

Editar: El hibernate.cache.provider_class puntos a mi proveedor de caché a medida que es una pasta de copia de SingletonEhCacheProvider y al final del método start() (después de la línea 136) que hago:

Ehcache cache = manager.getEhcache("foo"); 
if (!(cache instanceof BlockingCache)) { 
    manager.replaceCacheWithDecoratedCache(cache, new BlockingCache(cache)); 
} 

De esta manera, al inicializar, y antes de que nadie toque el caché llamado "foo", lo decoraré con BlockingCache. "foo" es un caché de consulta y "bar" (mismo código pero omitido) es un caché de entidad para un pojo.

Editar 2: "No parece que trabajar" significa que todavía existe el problema inicial. La memoria caché "foo" todavía se está rellenando muchas veces con los mismos datos, debido a la concurrencia. Valoro esto al enfatizar el sitio con JMeter con 10 hilos. Esperaría que los 9 hilos se bloquearan hasta que el primero solicitara datos de "foo" para finalizar su trabajo (ejecutar consultas, almacenar datos en el caché) y luego obtener los datos directamente del caché.

Datos 3: Otra explicación para este problema se puede ver en https://forum.hibernate.org/viewtopic.php?f=1&t=964391&start=0 pero con una respuesta definitiva.

+0

Un desarrollo interesante que puede ayudar a mitigar este problema es que ehcache ahora (desde 2.1) admite la estrategia de concurrencia de caché transaccional para hibernar: http://stackoverflow.com/questions/3472613/does-ehcache-2-1-support -the-transactional-cache-concurrency-strategy-in-hibernat/3474011 # 3474011 – cherouvim

Respuesta

1

La mayor mejora en este aspecto es que ehcache ahora (desde 2.1) supports the transactional hibernate cache policy. Esto mitiga enormemente los problemas descritos en este tema.

Con el fin de dar un paso más (lock hilos mientras se accede a la misma región caché de consultas) que uno necesita para implementar un QueryTranslatorFactory para volver personalizados (extendidas) QueryTranslatorImpl casos que inspeccionar la consulta y los parámetros y bloquear como sea necesario en la lista método. Por supuesto, esto se refiere al caso de uso específico de la memoria caché de consultas utilizando hql, que capta muchas entidades.

5

No estoy muy seguro, pero:

Permite el acceso de lectura simultánea a elementos que ya están en la memoria caché. Si el elemento es nulo, el otro leerá el bloque hasta que un elemento con la misma clave se coloque en el caché.

¿No significa que Hibernate esperará hasta que otro hilo coloque el objeto en el caché? Eso es lo que observas, ¿verdad?

Hib y la memoria caché funciona así:

  1. Hib consigue un pedido de un objeto
  2. cheques Hib si el objeto está en la memoria caché - caché.get()
  3. ¿No? Hib carga el objeto desde la BD y lo pone en la memoria caché - cache.put()

Entonces, si el objeto no está en caché (no fue colocado allí por alguna operación de actualización anterior), Hib esperaría 1) para siempre.

Creo que necesita una variante de caché donde el hilo solo espera un objeto durante un tiempo corto. P.ej. 100 ms. Si no se llega al objeto, el hilo debe ser nulo (y, por lo tanto, Hibernate cargará el objeto desde el DB y lo colocará en el caché).

En realidad, una mejor lógica sería:

  1. Compruebe que otro hilo está solicitando el mismo objeto
  2. Si es verdad, esperar durante largos (500 ms) para el objeto de llegar
  3. Si no es verdad , inmediatamente devuelto nulo

(no podemos esperar el 2 de siempre, como el hilo puede fallar para poner el objeto en caché - debido a excepción).

Si BlockingCache no es compatible con este comportamiento, debe implementar una caché usted mismo. Lo hice en el pasado, no es difícil, los métodos principales son get() y put() (aunque aparentemente API ha crecido desde entonces).

ACTUALIZACIÓN

En realidad, acabo de leer las fuentes de BlockingCache. Hace exactamente lo que dije: bloquear y esperar el tiempo de espera. Por lo tanto, no necesita hacer nada, simplemente úselo ...

public Element get(final Object key) throws RuntimeException, LockTimeoutException { 
    Sync lock = getLockForKey(key); 
    Element element; 
     acquiredLockForKey(key, lock, LockType.WRITE); 
     element = cache.get(key); 
     if (element != null) { 
      lock.unlock(LockType.WRITE); 
     } 
    return element; 
} 

public void put(Element element) { 
    if (element == null) { 
     return; 
    } 
    Object key = element.getObjectKey(); 
    Object value = element.getObjectValue(); 

    getLockForKey(key).lock(LockType.WRITE); 
    try { 
     if (value != null) { 
      cache.put(element); 
     } else { 
      cache.remove(key); 
     } 
    } finally { 
     getLockForKey(key).unlock(LockType.WRITE); 
    } 
} 

Así que es un poco extraño que no funcione para usted. Dime algo: en tu código este lugar:

Ehcache cache = manager.getEhcache("foo"); 

¿está sincronizado? Si hay varias solicitudes al mismo tiempo, ¿habrá solo una instancia de caché?

+0

Gracias por su respuesta. Todavía me cuesta creer que tengo que pasar a un nivel tan bajo con hibernate y ehcache para resolver este problema. – cherouvim

+0

No está sincronizado, pero solo se está llamando una vez. Vive bajo el último y vacío comienzo público de BlockingCacheProvider que se llama una sola vez antes de que algo comience. – cherouvim

Cuestiones relacionadas