Cachés

06/12/2011

Cachés en ataStore

Cachés

Las cachés son mecanismos que permiten aumentar enormemente la capacidad de carga de trabajo de un sistema, almacenando copias temporales de determinados elementos.

Hay múltiples niveles en los cuales podemos establecer cachés. A continuación expondremos dichos niveles y veremos cómo implementar cachés en nuestro sistema.

Notemos que no es conveniente habilitar cachés en la fase de desarrollo o en las primeras fases de producción, pues podrían ocultarnos problemas de rendimiento subyacentes, como por ejemplo consultas SQL mal hechas.

Cachés de MySQL

El gestor de bases de datos MySQL dispone de una caché de consultas. Los resultados de las consultas más recientes se almacenan en la caché, por lo que no tienen que calcularse se nuevo si realizamos la misma consulta.

Por defecto no viene habilitada en MySQL.

Podemos aprender más sobre la caché en el manual de MySQL y en el siguiente artículo de MySQL performance blog.

MySQL también recomienda su integración con la solución NoSQL Memcached

Cachés de Hibernate

Hibernate permite la configuración de dos tipos de cachés:

  • La caché de segundo nivel es un sistema para cachear los objetos Java para no tener que recuperar sus valores de la base de datos y crearlos cada vez.
  • La caché de consultas es un sistema para cachear los resultados de las consultas realizadas a través de Hibernate.

Configuración de las cachés de Hibernate

Para configurar estas cachés en primer lugar añadiremos las dependencias al pom.xml:

    
        org.hibernate
        hibernate-ehcache
        ${hibernate.version}
    
    
        net.sf.ehcache
        ehcache-core
        2.4.2
    

A continuación editaremos en el archivo WEB-INF/spring/servlet-context para establecer las propiedades de la clase LocalContainerEntityManagerFactoryBean:

    
        ...
        
            
                net.sf.ehcache.hibernate.Provider
                true
                true
            
         
         

En el ejemplo hemos usado como proveedor de caché EHCaché que, aunque tiene una configuración por defecto, también habría que configurar adecuadamente (ver ). Esto lo hacemos creando el archivo src/main/resources/ehcache.xml. Por ejemplo, con el siguiente archivo configuraríamos una caché sólo en memoria con una capacidad de 5000 elementos:

    
        
    

Nota: esta configuración se ha probado pero ahora no está activa en la aplicación.

Spring

Podemos integrar EHCache en Spring a través de anotaciones @Cacheable y @@TriggersRemove a través del proyecto Ehcache Annotations for Spring.

Configuración de la caché de Spring

En primer lugar añadiremos las dependencias al archivo pom.xml:

    
        net.sf.ehcache
        ehcache-core
        2.4.2
    
    
        com.googlecode.ehcache-spring-annotations
        ehcache-spring-annotations
        1.1.3
    

Ahora modificaremos el archivo e configuración de Spring WEB-INF/spring/servlet-context.xml añadiendo la declaración:

    
        ...
        
        
        ...
    

Y también creamos el archivo de configuración de EHCache src/main/resources/ehcache.xml:

    
    
        
        
    

Finalmente, así es como se usaría en nuestras clases Java:

    @Cacheable(cacheName = "productCache")
    public Product getProduct(Long productId) {
        ...
    }

    @TriggersRemove(cacheName="productCache")
    public void deleteProduct(Long productId) {
        ...
    }

Nota: sólo funciona en clases que implementen una interfaz. Sin embargo, en nuestro caso funciona bien en las DAO, no sé si es porque Spring las crea como proxies.

Importante: la caché está implementada con proxies, así que sólo funciona si se llama a métodos públicos desde fuera de la clase. No funcionará, por ejemplo, cuando un método llama a otro método de la misma clase.

La clave con la que se almacenan los elementos en la caché se calcula (por defecto) a partir de los parámetros que recibe el método que tiene la anotación @Cacheable. Así, si queremos que el elemento cacheado se invalide cuando lo modifiquemos el método que tiene la anotación @TriggersRemove debe recibir los mismos parámetros (o configurarlo adecuadamente con la anotación @KeyGenerator).

También podemos usar la propiedad removeAll y jugar con los nombres de las cachés:

    @TriggersRemove(cacheName="productCache", removeAll=true)
    public void save(Product requestProduct) {
        ...
    }

Thymeleaf

Estamos estudiando la posibilidad de desarrollar un sistema de cachés que pueda almacenar ciertos trozos de las páginas ya parseados (contenido HTML).

Hemos hecho algunas pruebas con el atributo ata:cache (elminado de de SVN en la revisión 34) pero aún no hemos desarrollado un sistema factible.

Apache

Para las páginas completas que sabemos que no tiene contenido dinámico podemos usar la caché de Apache.

También podemos establecerla para contenido dinámico para evitar grandes avalanchas de peticiones (por ejemplo, en una portada de un portal de noticias) siempre que no sea crítico que se mantenga cacheado unos segundos.

Navegador

Por último, una manera muy eficiente de limitar el consumo de nuestros servidores es asegurarnos de que el contenido que no cambia queda almacenado en la caché de los navegadores del cliente.

Para ello, las páginas deben tener las cabeceras adecuadas. En concreto:

  • Archivos CSS y imágenes usadas en estilos: al ser servidas por la clase ThemeInterceptor nos aseguramos que se setean las cabeceras. El tiempo de caché en la configuración se establece en WEB-INF/spring/servlet-context.xml:
        
            ...
            
        

    pero nosotros debemos setearlo en el archivo src/main/resources/config.properties:

        # ThemeResolver cache (css, images)
        themeResolver.cacheSeconds=3600
  • Imágenes (contenido) servidas por la aplicación: por documentar.
  • Archivos Javascript: por documentar.

 


Volver