2011-10-27 10 views
17

Tengo un requisito en el que estamos cargando datos estáticos de una base de datos para su uso en una aplicación Java. Cualquier mecanismo de caché debe tener la funcionalidad siguiente:Valores de precarga para un caché de Guava

  • carga todos los datos estáticos de la base de datos (una vez cargado, estos datos no cambiarán)
  • de carga nuevos datos de la base de datos (datos presentes en la base de datos en Inicio- hasta no va a cambiar, pero es posible añadir nuevos datos)

Lazy carga de todos los datos no es una opción ya que la aplicación se va a distribuir a múltiples ubicaciones geográficas y tendrá que comunicarse con una base de datos única. La carga lenta de los datos hará que la primera solicitud de un elemento específico sea demasiado lenta cuando la aplicación se encuentre en una región diferente de la base de datos.

He estado utilizando la API de MapMaker en Guava con éxito, pero ahora nos estamos actualizando a la última versión y parece que no puedo encontrar la misma funcionalidad en la API de CacheBuilder; Parece que no puedo encontrar una forma clara de cargar todos los datos en la puesta en marcha.

Una forma sería cargar todas las claves de la base de datos y cargarlas a través del caché individualmente. Esto funcionaría pero daría como resultado llamadas N + 1 a la base de datos, que no es la solución más eficiente que estoy buscando.

public void loadData(){ 
    List<String> keys = getAllKeys(); 
    for(String s : keys) 
     cache.get(s); 
} 

O la otra solución es utilizar una aplicación ConcurrentHashMap y manejar todos los hilos y las entradas que faltan a mí mismo? No estoy interesado en hacer esto ya que las API de MapMaker y CacheBuilder proporcionan el bloqueo de hilos basado en claves de forma gratuita sin tener que proporcionar pruebas adicionales. También estoy bastante seguro de que las implementaciones de MapMaker/CacheBuilder tendrán algunas eficiencias que desconozco/no tengo tiempo para investigar.

public Element get(String key){ 
    Lock lock = getObjectLock(key); 
    lock.lock(); 
    try{ 
     Element ret = map.get(key) 
     if(ret == null){ 
      ret = getElement(key); // database call 
      map.put(key, e); 
     } 
     return ret; 
    }finally { 
     lock.unlock(); 
    } 
} 

¿Alguien puede pensar en una mejor solución para mis dos requisitos?


Pedido de funciones

No creo pre-carga de una memoria caché es un requisito poco común, por lo que sería bueno si el CacheBuilder proporciona una opción de configuración para cargar previamente la memoria caché . Creo que proporciona una interfaz (al igual que CacheLoader) que poblar la memoria caché en el arranque sería una solución ideal, tales como:

CacheBuilder.newBuilder().populate(new CachePopulator<String, Element>(){ 

    @Override 
    public Map<String, Element> populate() throws Exception { 
     return getAllElements(); 
    } 

}).build(new CacheLoader<String, Element>(){ 

    @Override 
    public Element load(String key) throws Exception {  
     return getElement(key); 
    } 

}); 

Esta aplicación permitiría que el caché de ser pre-rellena con todo elemento relevante objetos, manteniendo el CustomConcurrentHashMap subyacente no visible para el mundo exterior.

+0

Agregue la solicitud de función a la lista de problemas de Guava. –

+1

Agregado (problema 775) – Richard

+1

http://code.google.com/p/guava-libraries/issues/detail?id=775 –

Respuesta

3

Cargaría todos los datos estáticos de la base de datos y los almacenaría en la memoria caché usando cache.asMap().put(key, value) ([Guava 10.0.1 permite operaciones de escritura en la vista Cache.asMap()] [1]).

Por supuesto, estos datos estáticos podrían obtener desalojados, si su caché está configurado para desalojar entradas ...

El CachePopulator idea es interesante.

+1

Eso funcionaría, pero estoy obligado a usar Guava 10.0.0, que usa el Implementación de ComputingCache $ CacheAsMap que lanza una UnsupportedOperationException si se llama a cualquiera de los métodos de modificación. Idealmente actualizaría, pero esa no es una opción en el momento – Richard

+3

Bueno, 10.0.1 era un pequeño arreglo de corrección de errores cuyo objetivo era volver a permitir cache.asMap(). Put(). Si no puede hacer una actualización tan pequeña, supongo que está brindando por ahora ... –

+2

Gracias por su optimismo :) – Richard

6

En el corto plazo solo usaría Cache.asMap().putAll(Map<K, V>).

Una vez que se libera Guava 11.0 puede usar Cache.getAll(Iterable<K>), que emitirá una única solicitud masiva para todos los elementos ausentes.

+10

Cache.getAll (Iterable ) no emite una única solicitud masiva de elementos ausentes. Según la API, emitirá una sola llamada para todas las claves suministradas, a menos que se modifique. Consulte http://code.google.com/p/guava-libraries/issues/detail?can=2&q=775&colspec=ID%20Type%20Status%20Milestone%20Summary&id=775 para tener una discusión sobre este tema. – Richard

Cuestiones relacionadas