Você está na página 1de 21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Cachs, concurrencia e Hibernate

Martn Prez Marin martin@javahispano.org

La arquitectura de cach de Hibernate es realmente potente. Su cach de dos niveles ofrece una gran flexibilidad al tiempo que un gran rendimiento tanto en sistemas en solitario como en cluster. Este artculo, trata de explicar el funcionamiento de ambas cachs, para que de este modo el lector pueda exprimir al mximo todas las posibilidades que ofrece Hibernate, aumentando as el rendimiento y escalabilidad de sus aplicaciones y evitando problemas de concurrencia.

1. Antes de empezar. Algo sobre Hibernate.


Sera realmente una aventura comenzar a leer este artculo sin tener unas nociones bsicas sobre Hibernate. Hibernate es una herramienta de asociacin entre modelos jerrquicos de objetos y esquemas relacionales de bases de datos, es decir, lo que se denomina comnmente como herramienta de ORM ( Object Relational Mapping ). Este tipo de herramientas permiten manejar la persistencia de nuestros objetos de una manera muy sencilla, a la vez que potente. Este artculo parte del supuesto de unos conocimientos bsicos sobre Hibernate. No es necesario ser un gur para asimilar los contenidos aqu presentes. Si tuviese que situarlo en algn rango de experiencia, dira que es un artculo de nivel bsico-medio.

Si no disponis de los conocimientos mnimos sobre Hibernate necesarios para afrontar la lectura de este artculo, os recomiendo los enlaces que aparecen al final para as poder obtener ms informacin.

1.1 La arquitectura de cach de Hibernate

La arquitectura de cach de Hibernate es muy potente. Hibernate, ofrece dos niveles de cach, orientados a tareas muy diferentes, y de los cuales, slo el primero de ellos es obligatorio. Por si fuese poco, Hibernate incluye una cach de consultas que nos da la posibilidad de obtener rpidamente resultados que ya haban sido consultados previamente.

El siguiente diagrama muestra un esquema de la cach de Hibernate, tal como se describe en su documentacin.

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

1/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Figura 1: Esquema de la cach de Hibernate.

2. Cachs y concurrencia
Una cach es un mecanismo que nos permite en muchas ocasiones aumentar el rendimiento de nuestras aplicaciones. Al almacenar nuestras estructuras de objetos en memoria, nos evita volver a buscar dichos objetos en base de datos, ahorrndonos multitud de accesos, y por consiguiente grandes cantidades de tiempo. Adems, otros usuarios, podrn compartir la cach de nuestro sistema, viendo incrementado todava ms el rendimiento de sus aplicaciones, al aprovecharse las estructuras de datos cargadas por otros usuarios.

Pero cualquier cach tiene asociados una serie de problemas que afloran especialmente al aumentar la concurrencia de las aplicaciones. Estos problemas, pueden llegar incluso a hacer que la cach se vuelva en nuestra contra, y que el rendimiento de las aplicaciones decrezca alarmntemente. Los no habituados a estos problemas supongo que ahora mismo se vern tentados a parar de leer este artculo.Cmo puede un sistema que tenga una cach funcionar peor que otro que no lo tengo!. Si es as, ruego tranquilidad. Espero que al finalizar este apartado est mucho ms clara esta cuestin.

He dicho ya que una cach almacena objetos en memoria para acelerar accesos posteriores. El procedimiento habitual sera el siguiente:

1. Un usuario realiza una consulta. 2. Se comprueba si los objetos estn en la cach. Si los objetos estn en la cach se devuelven directamente. 3. Si los objetos no estn en la cach, se recuperan de algn recurso externo ( base de datos, servicio web, servidor de apliacaciones ) y se almacenan en la cach para en un futuro nos ahorremos este paso, que es sin duda el ms costoso.

Pero a poco que nos pongamos a pensar, se nos ocurrirn una serie de problemas.

2.1 Problema 1 : Qu sucede si nos descargamos el objeto A de la cach y otro usuario ( que lo tiene tambin en su propia cach ) lo modifica?

La modificacin concurrente es el problema ms habitual en las cachs. Tal es la magnitud de este problema, que normalmente se recomienda el uso de cachs tan slo para datos de slo lectura, o para datos que se actualicen casualmente.

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

En nuestro ejemplo, si el segundo usuario utilizase la misma cach, no tendramos ningn tipo de problema, ya que dicha cach ya reflejara los cambios realizados por el primer usuario. Sin embargo los problemas surgen cuando las cachs son diferentes (algo comn en Hibernate como se explicar posteriormente), ya que en este caso el primer usuario encontrara que el objeto A ya est en su cach local y no lo recargara de la base de datos, perdiendo la actualizacin realizada por el segundo usuario. Asimismo nos encontrariamos con la engorrosa situacin de tener dos usuarios trabajando con objetos diferentes, y por si fuese poco, uno de los objetos est desincronizado con respecto a la base de datos y seguramente causar problemas.

2/21

23/07/13

objetos est desincronizado con respecto a la base de datos y seguramente causar problemas.

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Las soluciones a este problema son muy variopintas. El mecanismo de cach puede intentar realizar un chequeo cada cierto tiempo para comprobar el estado de los objetos en base de datos; tambin se podra tratar de sincronizar las cachs; otra solucin sera que el primer usuario eliminase los objetos de la cach antes de realizar la consulta, de modo que se fuerze siempre un fallo en la cach y se acceda al recurso externo; otra solucin sera realizar un acceso directo al recurso externo y evitar la cach en los casos en que sospechemos que pueden producirse actualizaciones concurrentes. Sea como sea, lo que est claro es que necesitamos algn mecanismo para controlar este molesto problema.

2.2 Problema 2 : Qu sucede en el caso anterior si nuestra cach es distribuida?

Otro problema importante es el que sucede cuando nuestra cach est distribuida en diferentes servidores. Un ejemplo de cach distribuida es la librera Open Source JbossCache, incluida dentro del servidor de aplicaciones Jboss, pero que puede ser utilizada por separado en cualquier otra aplicacin.

Las cachs distribuidas no slo tienen la carga asociada a la bsqueda de un objeto dentro de sus estructuras internas, sino que es necesario tener en cuenta tambin el tiempo empleado para sincronizar los diferentes nodos de la cach en las diferentes operaciones realizadas con la misma. Analizar el funcionamiento de las cachs distribuidas queda un poco fuera de este artculo, pero es necesario ser conscientes que el coste es mucho mayor que el presente en una cach con un nico nodo. Los sistemas de cache distribuida pueden ser muy variados. Podemos tener una cach replicada en todos los nodos, en cuyo caso tendramos un coste asociado de sincronizacin de los datos; podemos tener una cach repartida entre los nodos, en cuyo caso tendramos un coste asociado a la peticin de objetos a otros nodos; o quizs simplemente cachs independientes que se sincronizen peridicamente con una base de datos comn. Sea como sea, tenemos asociados una serie de costes de comunicacin que antes no estaban presentes.

El peor coste al trabajar con las cachs distribuidas es el asociado a las actualizaciones. En muchos casos, cada actualizacin realizada en un nodo, implica el comunicarse con el resto de nodos para que actualicen sus estructuras de datos. Evidentemente, esta actualizacin no ser trivial, y traer asociada una serie de transacciones entre los diferentes nodos; quizs a travs de un protocolo de comunicacin similar al commit en dos fases, y resumiendo, habr una serie de operaciones que incrementan considerablemente la latencia de nuestro sistema.

Como se puede apreciar, las cachs distribuidas tienen una serie de problemas graves, tanto si son redundantes como si no, que pueden ocasionar efectos contraproducentes en nuestro sistema. Muchas personas, podran creer que simplemente por el hecho de implantar una cach distribuida en su sistema, todo va a ir ms rpido. Sin embargo, si implantamos una cach en un sistema donde predominan las actualizaciones, seguramente nos encontraremos con el efecto contrario, y la latencia originada por dichas actualizaciones har que el rendimiento no slo no sea mayor, sino que se vea gravemente afectado.

2.3 Problema 3 : Qu sucede si el usuario que ha modificado el objeto A lo ha hecho desde una aplicacin que no usa la cach?

Nuevamente retomo aqu el problema de las actualizaciones remotas. Antes hablaba sobre lo que suceda si un segundo usuario modifica un objeto en su cach local. En el caso de que el segundo usuario no utilice una cach, algo que ser muy habitual ya que pocas veces tendremos el beneficio de un coto de caza privado sobre los datos de una empresa, los problemas que surgen son muy similares a los que planteaba en el primer caso. Aqu, ahora ya no podemos compartir la cach entre los dos usuarios, ya que las aplicaciones son diferentes, y probablemente hasta estn escritas en diferentes lenguajes o funcionen sobre diferentes plataformas.

El primer problema, tena varias soluciones, bastante sencillas de aplicar, pero parte del supuesto de que estamos trabajando con la misma aplicacin. Bajo este supuesto, todo es ms simple, ya que siempre podremos encontrar algn mecanismo de cooperacin
www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html 3/21

23/07/13

misma aplicacin. Bajo este supuesto, todo es ms simple, ya que siempre podremos encontrar algn mecanismo de cooperacin entre las cachs. Siempre tendremos algn tipo de acuerdo. Pero en el caso de que aplicaciones diferentes, y que no controlamos, puedan acceder a los datos que hemos almacenado en la cach, la cosa se agrava, hasta el punto de que no se recomienda utilizar cachs para acceder a este tipo de datos compartidos. Por qu? Pensemos en ejemplo: Imaginaros que tenemos una cach que accede a algn listado histrico de datos que se supona que nunca cambiaban, pero de pronto, llega una empresa diferente que le vende una aplicacin a nuestro cliente y resulta que dicha aplicacin permite la modificacin a mes vencido de dichos datos que suponamos eran invariables. Ese cambio seguramente le interesaba al cliente por la razn que sea, pero lo importante es que nosotros no nos hemos enterado, y a partir de ese momento nuestra aplicacin pasar a ofrecer datos potencialmente errneos, con los graves problemas que nos puede acarrear esto.

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Bsicamente, debemos recordar la regla de que si no somos dueos de los datos, entonces es mejor no guardarlos en la cach. Guardar en cach datos que no controlamos puede ser muy peligroso, por mucha confianza que tengamos en que no cambien. No debemos olvidar nunca que nosotros no somos los que mandamos en esos datos. Realizar un enorme esfuerzo en crear un fantstico mecanismo de cach, podra venirse al traste por algn problema de acceso concurrente a estos datos, y seguramente nos costara muchsimo ms esfuerzo despus el rehacer el trabajo, reescribiendo todo lo que haca la cach con otro sistema de acceso que no la utilice.

3. La cach de primer nivel


Aunque en un primer momento pueda parecer extrao, en Hibernate la cach de primer nivel se corresponde con el objeto de sesin ( net.sf.hibernate.Session ) que se obtiene de una factora de sesiones ( net.sf.hibernate.SessionFactory ). Esta cach de primer nivel guarda todos los objetos que vamos recuperando de la base de datos, de modo que si los volvemos a pedir, nos ahorramos un acceso a dicha base de datos. Adems de esto, esta cach de primer nivel tiene otras ventajas ya que representa un nico punto de acceso a los objetos, lo que evita representaciones conflictivas de los mismos, o que los cambios realizados en dichos objetos no sean visibles. Adems, como bien se comenta en la documentacin, esta sesin actua como fachada y situaciones como errores de accesos, lazos circulares en las referencias, etc., no afectarn a la totalidad del motor de persistencia sino que slo afectarn a la sesin involucrada, manteniendo a salvo el resto de sesiones abiertas.

Cuando realizamos operaciones find(), update(), save(), saveOrUpdate(), get(), delete(), y en general todas las operaciones ofrecidas por la interfaz Session, estamos interactuando de manera transparente con la cach de primer nivel, ya sea realizando consultas, actualizando objetos de la cach, eliminando objetos de la cach, etc.

Cuando realizamos operaciones de actualizacin, insercin o eliminacin de registros, estas operaciones no se realizan directamente, sino que se almacenan en la cach de primer nivel. Para volcar estas operaciones a base de datos es necesario realizar una llamada al mtodo flush() de la interfaz Session, o al mtodo commit() de la transaccin abierta para esa sesin(session.beginTransaction() ). En caso de haber colocado objetos en la cach que ya no queremos que estn ah, se puede utilizar el mtodo evict() que se elimine de la cach el objeto pasado como parmetro. Existe tambin un mtodo clear() que permite eliminar todos los objetos que se encuentren en ese momento en la cach, limpindola as por completo. Ambos mtodos, son tiles para polticas de sincronizacin entre cachs.

Cada vez que abrimos una sesin, se obtiene una conexin a base de datos del pool de conexiones de Hibernate. Esta conexin se libera cuando se cierra la sesin con el mtodo close(). Evidentemente, cuantas ms sesiones mantengamos abiertas, ms conexiones estaremos consumiendo del pool pudiendo dejarlo exhausto. Para solucionar esto, Hibernate permite desconectar una sesin momentneamente utilizando el mtodo disconnect(). Esta operacin, libera la conexin con la base de datos, aumentando efectivamente la escalabilidad de nuestro sistema al evitar el agotamiento del pool de conexiones . Evidentemente, existe un mtodoreconnect() que permite a la sesin volver a obtener otra conexin a base de datos del pool de conexiones.

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

Una vez explicados someramente los mtodos ms interesantes de la cach de primer nivel de Hibernate, es el turno de hablar sobre

4/21

23/07/13

Una vez explicados someramente los mtodos ms interesantes de la cach de primer nivel de Hibernate, es el turno de hablar sobre como utilizar la cach en nuestras aplicaciones. Existen diferentes variantes, segn el escenario en el que nos encontremos. Antes de explicarlas, convendra definir lo que en la documentacin de Hibernate se denomina unidad de trabajo, transaccin de aplicacin y aplicacin.

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

En Hibernate, una unidad de trabajo viene a representar una operacin con el servidor. Por ejemplo una operacin de actualizar el saldo de una cuenta corriente sera una unidad de trabajo.Por otra parte, una transaccin de aplicacin est formada por varias unidades de trabajo. Representa una operacin a mayor nivel realizada por el usuario. Por ejemplo, mover dinero de una cuenta corriente a otra, sera una transaccin de aplicacin, que constara de al menos dos unidades de trabajo de actualizacin de saldo. Para finalizar, en el contexto de una aplicacin se ejecutarn mltiples transacciones de aplicacin.

Una vez explicado esto, nos encontraramos con los siguientes modelos de gestin de la sesin, es decir, de la cach del primer nivel: sesin global, sesin por aplicacin , sesin por transaccin y sesin por proceso . He escogido este rden de presentacin de estos patrones porque me ha parecido el ms didctico. Se comienza con los patrones ms sencillos, y menos recomendados en cuanto a escalabilidad, y se termina con los patrones ms laboriosos, pero tambin ms escalables y utilizados comnmente. Todos tienen sus ventajas y desventajas, y espero que mis explicaciones os sirvan para escoger el ms apropiado para vuestras aplicaciones.

3.1 Sesin global

Figura 2: En una sesin global, las aplicaciones usan la misma sesin

Este es el escenario ms sencillo. Simplemente se crea una nica sesin en el servidor y esa sesin se encarga de recibir todas las peticiones y de realizar todas las operaciones con la base de datos. Esta aproximacin tiene la ventaja de que nunca se producirn errores de actualizacin concurrente entre cachs, ya que slo hay una y siempre estar actualizada. En caso de que alguna operacin produjese errores, o inconsistencias, se invalidara la sesin y se obtendra una nueva. Adems, es importante que se realicen peridicamente operaciones de flush() o commit(), ya que al tener una nica sesin, estamos manejando una nica transaccin, y una excepcin ocasionara un rollback() y perderamos todas las modificaciones hechas hasta el momento.

Evidentemente, esta sencillez tiene un precio. Esta solucin es la menos escalable de todas. A medida que crezca la carga del sistema, la situacin se volver inmanejable. Por ejemplo, si un usuario realizase una operacin costosa, que durase varios segundos, y llegasen varias peticiones de otros usuarios, stas tendran que esperar a que la operacin del primer usuario terminase. Evidentemente, tampoco se aprovecha el pool de conexiones que nos ofrece Hibernate ya que slo estamos utilizando una nica conexin como mximo, y adems sta conexin est siempre abierta.

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

5/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Esta es una solucin que nicamente ser interesante para aplicaciones que tengan poqusimos usuarios, y en las que no se prevean muchos ms. Adems es preferible que estos usuarios no trabajen simultneamente o, si lo hacen, que sea realizando operaciones en las que se consulten datos y el usuario permanezca un tiempo ocupado con esos datos, es decir, que no haya demasiado trabajo concurrente. En caso de que sea muy posible que la carga se incremente en un momento futuro, o que la concurrencia de la aplicasin sea moderada, lo mejor es utilizar directamente otra alternativa desde el principio, ya que as evitaremos el coste de una migracin. nicamente si se cumplen las premisas anteriormente citadas, esta solucin puede ser buena por simplicidad, pero antes de lanzarse a usarla, continuad leyendo el artculo porque hay algunos problemillas con esta implementacin de los que no he hablado y que le han otorgado el adjetivo de antipatrn.

3.2 Sesin por usuario o aplicacin

Figura 3: En una sesin por usuario, cada usuario tiene su propia sesin.

Este modelo, evolucin natural del anterior, propone la idea de asignar una sesin por cada usuario o por cada aplicacin cliente que se conecte con el servidor, al estilo de lo que un contenedor web hace con las sesiones HTTP. Una vez que se inicia la aplicacin se asocia una sesin con el usuario que se ha conectado al servidor. En el momento en el que se cierra la aplicacin, la sesin se cierra tambin. En estos sistemas siempre resulta interesante implementar algn tipo de control de timeout para evitar el problema de que se cierren aplicaciones cliente sin que el servidor se entere por problemas como cadas de tensin, reseteos, etc. Incluso un sistema de timeout como este nos puede ayudar a solucionar el problema de la inactividad en los clientes.

Esta aproximacin, exige una manejo inteligente de las operaciones disconnect() y reconnect() ya que de otro modo sera nuevamente muy sencillo que se agotase el pool de conexiones, o que se llegase al nmero mximo de conexiones permitido por nuestro servidor de base de datos. Ojo con las excepciones y errores. Recordad liberar las sesiones aunque se produzcan excepciones ya que si no quedarn latentes y se podra tener problemas de agotamiento en el pool.

La principal ventaja de esta aproximacin, es que mantenemos la cach de cada usuario activa en todo momento. La documentacin de Hibernate, recomienda cerrar la sesin una vez utilizada, despus de una transaccin de aplicacin o de una unidad de trabajo. Pero yo os pregunto: si, por ejemplo, me he descargado una jerarqua de diez mil objetos que ha tenido un coste de cien accesos a base de datos ( algo no muy raro ), que sucede si cierro la sesin y despus vuelvo a consultar la raiz de esa jerarqua? La respuesta es fcil: otros cien accesos a base de datos, y otros diez mil objetos nuevos. Imaginaros pues lo que sucedera con nuestra base de datos si muchos usuarios hiciesen eso mltiples veces. Sera muy probable que el rendimiento del servidor disminuyese.

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

6/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Pero entonces, por qu en Hibernate recomiendan cerrar la sesin? La respuesta es que Hibernate, como muchos otrosframeworks, est pensado para el mundo de las aplicaciones web, un mundo en el que nadie va a cargar jerarquas de diez mil objetos para mostrarlos en rboles y tablas, y un mundo en el que esperar diez segundos por una respuesta es algo habitual, por lo tanto la carga de volver a realizar consultas es asumible. En las aplicaciones de escritorio, las cosas son diferentes. Por qu diablos voy a tener que recargar los diez mil objetos que tena en la cach si ya los tena all guardados? Esa es la principal ventaja de esta aproximacin. Cada usuario tiene su cach de primer nivel, a la cual nos conectamos y reconectamos cuando sea conveniente, pero que siempre guarda lo que el usuario ha cargado y que permite que ste obtenga de forma rapidsima la informacin a la que ya ha accedido. Esta aproximacin nos permite crear aplicaciones realmente veloces!

Pero no todo poda ser tan bonito. El gran problema de esta aproximacin lo vimos antes: las actualizaciones concurrentes. Si cada usuario tiene su propia cach, entonces ignorar por completo los cambios realizados por otros usuarios concurrentes, resultando ello en graves riesgos potenciales. Existen diferentes soluciones para este problema, cada una con mayor o menor complejidad:

No hacer nada. Sin duda es la opcin ms sencilla. Sin embargo, slo es recomendable si nuestra aplicacin accede nicamente a datos de slo lectura, o que realmente no van a cambiar frecuentemente ( semanas, meses, etc. ). En este caso, no tiene demasiado sentido implementar un mecanismo de sincronizacin ya que no habr actualizaciones concurrentes. En caso de que haya datos de lectura/escritura frecuente, lo mejor es que olvidemos est solucin si no queremos perder nuestro empleo.

Realizar un mecanismo de sincronizacin entre sesiones. Esta quizs sea la solucin ms compleja. Cada vez que se realizase una operacin con una sesin (insercin, modificacin o eliminacin) habra que actualizar el objeto en el resto de sesiones, o simplemente eliminarlo de las mismas con una operacin evict(). Hibernate ofrece la posibilidad de crearinterceptores cuyos mtodos se ejecutaran justo antes de guardar, eliminar, actualizar objetos, o antes y despus de una operacin de flush(), lo que da una oportunidad para sincronizar las cachs.

Esta es la solucin que nos ofrecera una mayor fiabilidad en los datos, aunque realmente es compleja, sobre todo cuando hay que manejar jerarquas de objetos. As, que si os disponis a aplicar esta solucin, prepararos para un trabajo laborioso. Una alternativa similar, y mucho ms recomendable, es utilizar una cach de segundo nivel y nos puede ahorrar bastante trabajo, ya que ella misma realizar esta sincronizacin.

Ofrecer operaciones de refresco o limpieza . Otra posibilidad es que la aplicacin pueda asumir que haya usuarios que trabajan concurrentemente, y que adems el usuario sea consciente de ello. En este caso, en ciertas operaciones invocables por el usuario, se eliminara el objeto de memoria, se vaciara la cach, o se refrescara el estado de los objetos solicitados. Cualquiera de las operaciones desemboca en que cuando el usuario consulte de nuevo el objeto eliminado de la cach o refrescado, se acceder a base de datos, y por lo tanto se recuperar el valor correcto del mismo. Una variante similar sera cerrar la sesin en lugar de realizar una llamada a clear() o refresh().

Operaciones como refrescar o cargar de nuevo son muy habituales en los interfaces de usuario. Un ejemplo sera los interfaces de usuario de acceso a repositorios de CVS, en las que todos estamos acostumbrados a refrescar para obtener los cambios.
www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html 7/21

23/07/13

de usuario de acceso a repositorios de CVS, en las que todos estamos acostumbrados a refrescar para obtener los cambios. Una alternativa a que sea el usuario el que explcitamente realice estas operaciones, es que sea la aplicacin cliente la que cada cierto tiempo vacie la cach, recargue ciertas estructuras de datos, etc. Si el mrgen de tiempo que nos da las actualizaciones realizadas es suficiente, nuestro sistema se comportar de manera excelente de modo transparente al usuario.

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Esta solucin es ideal para aplicaciones, como un cliente CVS por ejemplo, en las que el usuario sea consciente de que se est realizando un trabajo concurrente y en el que la concurrencia no sea demasiado habitual. Asumir esto, nos evita tener que gestionar nosotros la concurrencia o aadir una cach de segundo nivel, y nos evita cualquier tipo de problema de sincronizacin que pueda aparecer, as que es una solucin muy atractiva. Pero ojo, recordemos que los usuarios cambian mucho de opinin. Pensad siempre, que si de pronto la aplicacin tiene mucho xito y en vez de diez usuarios pasamos a tener mil, la concurrencia sera mucho mayor, y todo lo que hemos hecho puede irse al traste. As, que si intus una situacin as, recordad que ms vale prevenir.

Utilizar una cach de segundo nivel. Las cachs de primer nivel pueden utilizar una cach de segundo nivel como apoyo. Una cach de segundo nivel sirve como cach global para las cachs de primer nivel. Esta alternativa, nos permite cerrar las sesiones como recomienda la documentacin de Hibernate, ya que ahora aquellos cientos de consultas a base de datos de las que hablabamos en el ejemplo de este apartado ya no se produciran, sino que se cargaran los datos directamente de la cach de segundo nivel. Evidentemente, volvemos a tener un coste mayor que si obtuvisemos los datos directamente desde la cach de primer nivel, pero sin duda es un coste mucho ms asumible que el de acceder a base de datos constntemente.

Personalmente, esta opcin es la que yo recomendara, siempre que dispongamos de una cach de segundo nivel que se adapte a nuestras necesidades, ya que es muy sencilla de implementar, nos mantiene dentro de los patrones recomendados y que veremos en los siguientes apartados, el coste de los fallos de cach de primer nivel se ve amortiguado considerablemente, y mantenemos un sistema escalable en previsin de aumentos posteriores de carga en el sistema.

3.3 Demitificando antipatrones y resolviendo problemas

Voy a hacer un lapsus en la explicacin de los diferentes modelos, para romper una lanza en favor de las dos aproximaciones anteriores, que no son del agrado de los desarrolladores de Hibernate. Antes he comentado que esto es porque el mundo web tiene unos requisitos diferentes que el de las aplicaciones de escritorio, as que la documentacin de Hibernate, claramente orientada a la web, utiliza un punto de vista no demasiado imparcial. Bueno, esto es as, pero tambin sera justo reconocer una serie de limitaciones en estas aproximaciones, de las que no he hablado y que han servido para colgarle el apodo de antipatrones a estos modelos. Ms concretamente, estos dos modelos se corresponden con el antipatrn session-per-application y session-per-userapplication .

Antes de que me colguis por haber explicado dos modelos que Hibernate considera como antipatrones, explicar cuales son sus razones, y veremos que realmente es un apodo absolutamente injusto, y derivado de un modelo de desarrollo de aplicaciones el web, que proporciona una visin muy limitada del espectro real de las aplicaciones en las que se puede utilizar Hibernate. Las razones principales por las que estos modelos se consideran antipatrones son las siguientes ( extraidas de la documentacin de Hibernate ):

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

1. Una sesin de Hibernate no es thread-safe. Esto hace que si dos usuarios acceden concurrentemente a la misma sesin,

8/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

1. Una sesin de Hibernate no es thread-safe. Esto hace que si dos usuarios acceden concurrentemente a la misma sesin, o un usuario accede a su sesin mientras est ejecutando otro mtodo ( por ejemplo, al pulsar rpidamente varias veces el botn de submit de un formulario ), se podran originar graves inconsistencias. Realmente esta razn se cae por su propio peso. Con un planteamiento similar podramos decir que no se deben utilizar Servlets porque no son threadsafe (SingleThreadModel s que es un verdadero antipatrn ), algo que sera totalmente absurdo.

Alguin ha dicho que crear un servidor fuera algo sencillo y que no necesitemos establecer mecanismos de sincronizacin? Resulta bastante claro, que en cualquier servidor, utilicemos Hibernate o cualquier otra herramienta, tendremos que establecer una serie de controles sobre el acceso concurrente, como por ejemplo utilizar bloques de sincronizacin en las partes crticas. Pero esto es algo que deberamos hacer de todos modos, tengamos una sesin por aplicacin, por usuario, por transaccin o por unidad de trabajo. Por otra parte, la ejecucin de acciones repetidas por pulsar dos veces un botn, es algo que claramente debemos controlar en el cliente.

2. Desincronizacin en caso de excepciones. Esto, ms que una razn para declarar algo un antipatrn, es simplemente un problema. El problema de la desincronizacin sucede cuando se produce alguna excepcin. Si se da este caso, se producir un rollback en la transaccin de Hibernate y se cerrar la sesin. Lo peor es que el estado de los objetos no coincidir con el estado de la base de datos, y nos encontramos ante una desincronizacin. Esto hace que despus no podamos reasociar (attach) estos objetos con la sesin abierta, probablemente debido a una desincronizacin en las jerarquas.

Pero la solucin a esto no es tan traumtica, y es un ejercicio bsico de recuperacin de errores para cualquier sistema. Por una parte, en caso de una excepcin, deberamos crear una nueva sesin totalmente limpia. A continuacin, y slo en el caso de que estuvisemos trabajando con algn objeto en el momento de producirse la excepcin, lo volveramos a sincronizar con una operacin update().

3. La sesin crece indefinidamente . Nuevamente estamos ante un problema claro de las aplicaciones web. Las aplicaciones web, por diversas razones, suelen tener una carga de usuarios y una concurrencia mucho mayor que las aplicaciones de escritorio. Esto hace que lleguemos a situaciones en algunos casos de miles o decenas de miles de usuarios accediendo concurrentemente a un servidor. En estos casos, utilizar una cach por aplicacin o por usuario, es realmente un error gravsimo. Hacer esto, ocasionara que no parsemos de almacenar datos y datos en las diferentes cachs, y la memoria crecera sin parar hasta lanzar la siempre temible OutOfMemoryException. Incluso, aunque aadisemos un listener a laHttpSession de modo que se liberase la sesin de un usuario al caducar su sesin web, tendramos los mismos problemas si el nmero de accesos concurrente es lo suficientemente grande.

Pero nuevamente estamos ante un escenario entre los muchos que existen. En aplicaciones con veinte o treinta usuarios, no hay unos riesgos tan grandes. Lo que si que es cierto es que an as hay que tener cuidado. Hemos de ser conscientes de que si cargamos un histrico de una nmina con cientos de miles de registros, Hibernate guardar esos objetos en la sesin, y ah se quedarn hasta que se cierre la sesin. Ante estos casos, lo mejor es coger y eliminar el objeto de la cach con una operacin evict(), o simplemente limpiar la cach.

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

9/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Una norma fundamental cuando se utilizan cachs, es almacenar slo lo que estamos seguros de que se reutilizar. En otro caso, lo mejor es no utilizar la cach. Cuando trabajis con una sesin y la mantengis activa en memoria, recordad bien esto. Recordad que es una cach de primer nivel, y eliminad de ella todo lo que creis que no vale la pena que est ah. Por ejemplo, los datos de un empleado que se fue de la empresa hace diez aos, puede que hagan falta una vez, pero es poco probable que estemos continuamente consultndolos.

Resumiendo. En mi opinin, estos dos modelos no son antipatrones, son simplemente alternativas. Alternativas que poseen unas ventajas y unas desventajas, y que debemos meditar bien su utilizacin en base a las caractersticas concretas de nuestro escenario de trabajo. Los dos siguientes modelos se denominan en Hibernate session-per-application-transaction y session-perrequest(nuevamente una referencia a la naturaleza web de Hibernate), y ofrecen una escalabilidad mucho mayor que los dos que hemos visto hasta ahora, pero que sin embargo, no aprovechan tan bien la cach de primer nivel, a no ser que dispongamos de una cach de segundo nivel respaldndolos.

3.4 Sesin por unidad de trabajo

Figura 4: Cada unidad de trabajo utiliza una sesin.

Este modelo de gestin de la sesin es el que tiene una granularidad ms fina. En una sesin por unidad de trabajo, cada vez que se ejecuta un mtodo, se abre una nueva sesin de Hibernate. La sesin abierta, se cierra al finalizar la unidad de trabajo, momento en el que finaliza la transaccin abierta. Idealmente, una unidad de trabajo constara de varios accesos a base de datos, ya que en otro caso ya entraramos dentro de lo que en la documentacin de Hibernate denominan como el antipatrn session-per-operation ; entaramos en el caso de abrir y cerrar una sesin por cada acceso a base de datos, que obviamente no nos aportara absolutamente ningn beneficio en cuanto a cach de objetos.

Es bastante claro que esta aproximacin es la que ofrece una mayor escalabilidad y una mayor tolerancia a fallos. Por una parte, las conexiones se estn recogiendo y devolviendo en intervalos cortos al pool de conexiones, por lo que es difcil que ste quede exhausto. Por otra parte, las transacciones con la base de datos son cortas, y esto disminuye bastante la contencin de nuestro sistema, ya que las necesidades de sincronizacin, bloqueos, etc., disminuyen considerablemente. Por ltimo, una excepcin no provoca problemas tan graves como en los casos anteriores. Ahora, en caso de un fallo, eliminaramos la sesin y recuperaramos otra nueva. Una operacin de rollback() slo afectara a la unidad de trabajo, lo que nos permite recuperarnos de manera ms sencilla del fallo y provoca menores desincronizaciones.

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

La gran desventaja de esta aproximacin es que no se aprovecha para nada la cach de primer nivel. Nuestra cach durar lo que dur una unidad de trabajo, es decir, unas cuantas operaciones. Y cuando termine dicha unidad, nada de lo que hayamos obtenido estar disponible de nuevo para un acceso rpido, teniendo que volver a acceder a base de datos para recuperar los datos anteriormente cargados. En una aplicacin web, esto no es algo demasiado grave, ya que se asume ya un tiempo de latencia

10/21

23/07/13

anteriormente cargados. En una aplicacin web, esto no es algo demasiado grave, ya que se asume ya un tiempo de latencia importante. Sin embargo, en aplicaciones de escritorio podemos tener problemas ya que los usuarios esperan una latencia mucho menor. Esta solucin es ideal para entornos que necesiten una gran escalabilidad y que no muevan grandes jerarquas de datos que merezcan permanecer en cach. En tal caso, estamos ante la eleccin correcta. Aplicaciones web, o de aplicaciones escritorio en las que cada unidad de trabajo suponga tan slo unos cuantos accesos a base de datos, son candidatas perfectas a utilizar este patrn, ya que los beneficios son importantes. Otra gran ventaja de este modelo es que los problemas de actualizacin concurrente disminuyen enormemente. Si las unidades de trabajo son lo suficientemente cortas, ser difcil que haya inconsistencias, e incluso las podramos eliminar completamente con un esquema de sincronizacin, ya que ahora al trabajar con unidades de trabajo cortas, no causaramos demasiada contencin con los bloqueos.

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Ahora bien, si tenemos aplicaciones que acceden continuamente a las mismas jerarquas de datos complejas, las cules requieran decenas, cientos o incluso miles de consultas, entonces debemos considerar seriamente el utilizar uno de los modelos anteriores o, implantar una cach de segundo nivel. En dichos casos, no podemos permitirnos tantos fallos en la cach de primer nivel. En el caso contrario los fallos en la cach de primer nivel son aceptables y se ven compensados por el aumento en la escalabilidad y la tolerancia a fallos.

3.5 Sesin por transaccin de aplicacin

Figura 5: Cada transaccin de aplicacin utiliza una sesin.

Este modelo es una combinacin de los que hemos visto hasta ahora. En una sesin por transaccin de aplicacin, la sesin de Hibernate se mantiene abierta mientras dura la transaccin. Entre las diferentes unidades de trabajo, se utilizan los mtodosdisconnect() y reconnect(), para que las conexiones vuelvan al pool y mantener la escalabilidad. Una vez que termina la transaccin de aplicacin, se cierra definitivamente la sesin. Se trata de un modelo claramente orientado a un entorno web (
www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html 11/21

23/07/13

transaccin de aplicacin, se cierra definitivamente la sesin. Se trata de un modelo claramente orientado a un entorno web ( raramente se encuentran este tipo de transacciones en las aplicaciones de escritorio, salvo en los tpicos wizards ) , donde a menudo muchas operaciones requieren secuencias de mltiples pasos, formularios, sistemas de compra online, etc. Un ejemplo podra ser el realizar una transaccin bancaria de una cuenta de un banco a otra, en donde primero se seleccionara la cuenta rigen, despus la cantidad, despus el destino, y finalmente se realizse la transaccin bancaria. Esto se representara como una transaccin de aplicacin que constara de cuatro unidades de trabajo. Mientras durase el proceso, los diferentes objetos como la cuenta, los importes, los clientes, etc., estaran en la sesin.

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Al ser una mezcla de varios modelos, esta aproximacin aprovecha muchas de sus ventajas. Por una parte, la escalabilidad es mayor que en las dos primeras aproximaciones que tratamos, ya que la sesin permanece nicamente abierta durante el tiempo que dura la transaccin de aplicacin. Por otra parte, lo ms normal es que las diferentes unidades de trabajo de la transaccin de aplicacin estn relacionadas y manejen los mismos objetos. Como esos objetos estn en la cach de primer nivel mientras la transaccin de aplicacin se ejecuta, estamos aprovechando mucho mejor la cach de lo que se haca en el modelo de sesin por unidad de trabajo.

Pero este modelo de mezcla, presenta tambin algunas desventajas. Si nuestras transacciones de aplicacin son cortas, volveremos a estar desaprovechando nuestra cach de primer nivel. Por otra parte, si nuestras transacciones son muy largas, podemos estar incurriendo en el problema de las actualizaciones concurrentes que tenan los primeros modelos. Esto hace que, si alguno de estos problemas es realmente muy importante, puede ser mejor el irnos directamente a alguno de los otros modelos para eliminar el problema, y evitar trabajar de este modo hbrido.

4. La cach de segundo nivel


Como ya hemos visto, el problema de las actualizaciones concurrentes supone un grave quebradero de cabeza para cualquier sistema de cach. Sea cual sea el modelo que uilicemos, siempre tendremos problemas con las actualizaciones concurrentes, en mayor o menor medida. La gran ventaja de una cach de segundo nivel, es que nos ayuda a mitigar estos problemas, al tiempo que alivia de la responsabilidad de almacenar datos temporales, a la cach de primer nivel; responsabilidad que, por otra parte, era la causa de que los modelos de sesin por transaccin de aplicacin y de sesin por unidad de trabajo pudiesen enlentecer nuestras aplicaciones.

Una cach de segundo nivel se acoplar a la sesin de Hibernate absorbiendo todos los fallos que se produzcan en sta. Como ya he comentado, la gran ventaja de utilizar una cach de segundo nivel es que desaparecen los problemas de actualizaciones concurrentes entre sesiones, es decir, el primer problema que se nos planteaba hace ya unos cuantos apartados. La cach de segundo nivel se situa al mismo nivel que el objeto SessionFactory de Hibernate, recogiendo y coordinando los objetos con los que trabajan las diferentes sesiones.

La recomendacin general con una cach de segundo nivel es utilizar una aproximacin de sesin por transaccin de aplicacin o de sesin por unidad de trabajo, segn nos convenga. Con una cach de segundo nivel, el grave problema de no aprovechamiento de la cach de primer nivel del que adelolecan estos modelos, desaparece parcialmente. Ahora, si un objeto no se encuentra en la cach de primer nivel, Hibernate tratar de obtenerlo de la cach de segundo nivel, de modo que en caso de encontrarse ah nos habremos ahorrado un hit en la base de datos. Cerrar las sesiones pues, ya no es un problema tan grave, ya que aunque tengamos que realizar cientos y cientos de consultas recurrentes, los datos estarn en la cach de segundo nivel, y la perdida de rendimiento ya no ser tan grande.

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

12/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Figura 6: La cach de segundo nivel absorbe la mayora de fallos de las cachs de primer nivel

Despus del sufrimiento que pareca que nos deparaba el uso de la cach de primer nivel, al no solucionarnos apenas problemas, aparentemente la cach de segundo nivel es la panacea. An as, es muy importante tener en cuenta que es necesario tener una serie de precauciones con esta cach. Por una parte est el tema de las cachs distribuidas de las que hablar posterioremente. Por otra parte, hemos de tener siempre en mente que no todos nuestros objetos se beneficiarn del uso de una cach de segundo nivel. Normalmente, para cualquier tipo de cach se recomienda almacenar en su interior datos que no cambien con demasiada frecuencia. Asimismo, determinados datos histricos puede que no nos valga la pena almacenarlos en la cach por el espacio que puedan llegar a consumir. Por ejemplo, tendra sentido almacenar un listado de artculos del ao 1991 con cientos de miles de registros en la cach de segundo nivel? Probablemente no. Afortunadamente, los sistemas de cach de segundo nivel suelen ofrecer mecanismos de configuracin que permiten filtrar la informacin que se almacenar en la cach, su caducidad, el lmite mximo de memoria ocupada, etc.

4.1 La cach en hibernate y estrategias de concurrencia

Hibernate permite habilitar individualmente la cach para cada una de nuestras entidades. De este modo, podemos decidir que clases se beneficiarn del uso de una cach, ya que como explicaba anteriormente, puede que no todas las clases de nuestro sistema se beneficien. Adems de esto, al habilitar la cach es necesario establecer la estrategia de concurrencia que Hibernate utilizar para sincronizar la cach de primer nivel con la cach de segundo nivel, y sta ltima con la base de datos. Hay cuatro estrategias de concurrencia predefinidas. A continuacin aparecen listadas por rden de restricciones en trminos de aislamiento transaccional.

transactional: Garantiza un nivel de aislamiento hasta repeatable read, si se necesita. Es el nivel ms estricto. Se recomienda su uso cuando no podamos permitirnos datos que queden desfasados. Esta estrategia slo se puede utilizar en clusters, es decir, con cachs distribuidas.

read-write : Mantiene un aislamiento hasta el nivel de commited, utilizando un sistema de marcas de tiempo (timestamps). El caso de uso recomendable es el mismo que para la estrategia transactional pero con la diferencia de que esta estrategia no se puede utilizar en clusters.

nonstrict read-write : No ofrece ninguna garanta de consistencia entre la cach y la base de datos. Para sincronizar los objetos de la cach con la base de datos se utilizan timeouts, de modo que cuando caduca el timeout se recargan los datos. Con esta estrategia pus, tenemos un intervalo en el cual tenemos el riesgo de obtener objetos desfasados. Cuando Hibernate
www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html 13/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

realiza una operacin de flush() en una sesin, se invalidan los objetos de la cach de segundo nivel. An as, esta es una operacin asncrona y no tenemos nunca garantas de que otro usuario no pueda leer datos errneos. A pesar de todo esto, esta estrategia es ideal para almacenar datos que no sean demasiado crticos.

read-only: Es la estrategia de concurrencia menos estricta. Ideal para datos que nunca cambian.

A medida que bajamos en esta lista, el rendimiento aumenta, ya que disminuye la necesidad de sincronizacin. Es posible definir una estrategia de concurrencia personalizada implementando la interfaz net.sf.hibernate.cache.CacheConcurrencyStrategy, pero la documentacin advierte de que se trata de una tarea complicada y slo apta para extraos casos de optimizacin.

En Hibernate, no slo hay que marcar una clase como almacenable en cach. Adems, todas las colecciones de datos que deseemos guardar las tendremos que marcar, seleccionando tambin para ellas una estrategia de concurrencia. No todas las colecciones soportan la etiqueta <cache>, sino que esta etiqueta es aplicable slo a los elementos set, map, bag, idbag, array,primitive-array y list. El siguiente ejemplo muestra una clase y su definicin de cach de segundo nivel:

< c l a s sn a m e = " o r g . j l i b r a r y . c o r e . e n t i t i e s . R e p o s i t o r y " t a b l e = " R E P O S I T O R I E S " >

< c a c h eu s a g e = " r e a d w r i t e " / >

< i dn a m e = " i d "c o l u m n = " i d "t y p e = " s t r i n g " > < g e n e r a t o rc l a s s = " u u i d . h e x "/ > < / i d >

< p r o p e r t yn a m e = " n a m e "t y p e = " s t r i n g "/ > < p r o p e r t yn a m e = " d e s c r i p t i o n "t y p e = " s t r i n g "/ > < p r o p e r t yn a m e = " p a t h "t y p e = " s t r i n g "/ > < p r o p e r t yn a m e = " c r e a t o r "t y p e = " s t r i n g "/ > < m a n y t o o n ec a s c a d e = " d e l e t e " n a m e = " r o o t " c o l u m n = " r o o t " c l a s s = " o r g . j l i b r a r y . c o r e . e n t i t i e s . D i r e c t o r y " / >

< s e tn a m e = " c a t e g o r i e s "

i n v e r s e = " t r u e " c a s c a d e = " d e l e t e " >

< c a c h eu s a g e = " r e a d w r i t e " / >

< k e yc o l u m n = " r e p o s i t o r y " / > < o n e t o m a n yc l a s s = " o r g . j l i b r a r y . c o r e . e n t i t i e s . C a t e g o r y " / > < / s e t > < / c l a s s >

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

14/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

4.2 Proveedores de cach Despus de decidir que clases vamos a guardar en la cach segundo nivel y como las vamos a guardar, nos falta decidir cual va a ser nuestro proveedor de cach y como se va a comportar. Hibernate no incluye ningn proveedor de cach de segundo nivel, pero si que soporta varios. Ni que decir tiene, que un proveedor de cach no es ms que una librera donde se implementan los diferentes algoritmos de la cach de segundo nivel. Existen varios proyectos Open Source que nos ofrecen diferentes proveedores de cach de segundo nivel, y que Hibernate soporta. La tabla siguiente muestra los proveedores soportados y su descripcin.

Descripcin Cach simple. No posible en cluster. Cach en memoria o EHCache disco. Soporta la cach de resultados. OSCache Cach en memoria o disco. No cluster. Muy configurable. Soporte de cluster gracias a JGroups. No soporta la cach SwarmCache de consultas Soporte de cluster basado en JGroups. Soporte de cach JBossCache de consultas.

Proveedor

Estrategias de concurrencia soportadas read-only, nonstrict-read-write, read-write read-only, nonstrict-read-write, read-write read-only, nonstrict-read-write read-only, transactional

Existe

la

posibilidad

de

adaptar

otros

productos

de

cach

simplemente

implementando

la

interfaznet.sf.hibernate.cache.CacheProvider.

Una vez escogido el producto que ms nos convenga, habr que configurarlo para que funcione en Hibernate. Como mnimo, tendremos que modificar el fichero hibernate.properties para avisar a Hibernate para que utilice el proveedor de cach escogido:
www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html 15/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

h i b e r n a t e . c a c h e . p r o v i d e r _ c l a s s = n e t . s f . e h c a c h e . h i b e r n a t e . P r o v i d e r Adems, segn el proveedor de cach tendremos que realizar alguna otra tarea. Por ejemplo, con EHCache, es necesario colocar el fichero ehcache.xml en el classpath de la aplicacin.

Una vez escogido y configurado el proveedor, deberamos establecer las polticas de cach. Esta tarea ya depende de cada uno de los proveedores. Uno de los proveedores ms sencillos es EHCache, que adems ofrece un rendimiento bastante aceptable, as que ser el que utilice para este ejemplo.

EHCache se configura mediante el fichero ehcache.xml. Este fichero de configuracin es muy simple. Ah se define:

El directorio de disco donde se guardar los elementos de la cach de segundo nivel que no cojan en memoria. Las polticas por defecto para la cach de segundo nivel. Cada una de las polticas de cach personalizadas para las clases que deseemos.

EHCache nos permite configurar diferentes parmetros dentro de las polticas de cach. Para cada poltica de cach se requieren los siguientes atributos:

name: Nombre de la cach. Ha de ser nico. maxElementsInMemory: Nmero mximo de objetos que se crearn en memoria. eternal: Marca que los elementos nunca expirarn de la cach. Sern eternos y nunca se actualizarn. Ideal para datos inmutables. overflowToDisk : Marca que los objetos se guarden en disco si se supera el lmite de memoria establecido.

Los siguientes atributos son opcionales:

timeToIdleSeconds: Es el tiempo de inactividad permisible para los objetos de una clase. Si un objeto sobrepasa ese tiempo sin volver a activarse, se expulsar de la cach. timeToLiveSeconds: Marca el tiempo de vida de un objeto. En el momento en que se sobrepase ese tiempo, el objeto se eliminar de la cach, independientemente de cuando se haya usado. diskPersistent: Establece si los objetos se almacenarn a disco. Esto permite mantener el estado de la cach de segundo nivel cuando se apaga la JVM, es decir cuando se cae la mquina o se para el servidor o la aplicacin. diskExpiryThreadIntervalSeconds: Es el nmero de segundos tras el que se ejecuta la tarea de comprobacin de si los

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

16/21

23/07/13

diskExpiryThreadIntervalSeconds: Es el nmero de segundos tras el que se ejecuta la tarea de comprobacin de si los elementos almacenados en disco han expirado.

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Este rango de opciones nos ofrece una amplia posibilidad de variantes. Un ejemplo de configuracin sera el siguiente:

< e h c a c h e > < ! -A q u p o d e m o su t i l i z a ru n aU R Ld ed i s c ool a sp r o p i e d a d e su s e r . h o m e ,u s e r . d i roj a v a . i o . t m p . d i r> < d i s k S t o r ep a t h = " j a v a . i o . t m p d i r " / > < d e f a u l t C a c h e

m a x E l e m e n t s I n M e m o r y = " 1 0 0 0 0 " e t e r n a l = " f a l s e " t i m e T o I d l e S e c o n d s = " 1 2 0 " t i m e T o L i v e S e c o n d s = " 1 2 0 " o v e r f l o w T o D i s k = " t r u e " d i s k P e r s i s t e n t = " f a l s e " d i s k E x p i r y T h r e a d I n t e r v a l S e c o n d s = " 1 2 0 " / > < c a c h en a m e = " o r g . j l i b r a r y . c o r e . e n t i t i e s . U s e r " m a x E l e m e n t s I n M e m o r y = " 1 0 0 0 " e t e r n a l = " f a l s e " o v e r f l o w T o D i s k = " t r u e " t i m e T o I d l e S e c o n d s = " 3 0 0 " t i m e T o L i v e S e c o n d s = " 1 2 0 0 " / > < c a c h en a m e = " o r g . j l i b r a r y . c o r e . e n t i t i e s . H i s t o r y R e g i s t r y " m a x E l e m e n t s I n M e m o r y = " 1 0 0 " e t e r n a l = " t r u e " o v e r f l o w T o D i s k = " t r u e " t i m e T o I d l e S e c o n d s = " 1 8 0 " t i m e T o L i v e S e c o n d s = " 3 0 0 " / > < / e h c a c h e >

5. Caches distribuidas
Durante todo este artculo, se ha dejado apartado el tema de las cachs distribuidas para el final. Una aplicacin empesarial con decenas de miles de usuarios, es posible que necesite ejecutarse en un entorno distribuido, es decir, en un cluster. Llegado a ese momento, el nico elemento de Hibernate que necesita configurarse es la cach de segundo nivel.

Hay dos modos de montar una cach de segundo nivel de Hibernate en un cluster. El ms sencillo, sin ninguna duda, es colocar en cada nodo del cluster una instancia del proveedor de cach, por ejemplo de EHCache, y confiar en los timeout para la sincronizacin de los proveedores de cach de los diferentes nodos. Este sistema es muy simple, y no presenta retardos de sincronizacin entre los nodos, ya que no existe sincronizacin alguna. El segundo modo, es instalar un proveedor de cach ms avanzado que soporte la sincronizacin de las cachs de los diferentes nodos. El proveedor recomendado por Hibernate para esta tarea es JBossCache, un provedor totalmente transaccional basado en la librera de multicasting, JGroups.
www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html 17/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Figura 7: Diferentes esquemas de trabajo de una cach distribuida en Hibernate. Como en el caso de EHCache, para activar el proveedor de cach es necesario aadir una lnea al fichero de configuracin de hibernate:

h i b e r n a t e . c a c h e . p r o v i d e r _ c l a s s = n e t . s f . h i b e r n a t e . c a c h e . T r e e C a c h e P r o v i d e r

JBossCache se configura utilizando el fichero treecache.xml que ha de estar presente en cada uno de los nodos del cluster. Este fichero tiene diferentes partes, y es bastante ms complejo que el de EHCache ya que necesita especificar la configuracin delcluster, las polticas de comunicacin entre los nodos, etc. El siguiente ejemplo, extraido del libro Hibernate in Action, muestra como sera la configuracin de JBossCache en cuanto a la poltica de cach y sus regiones:

< s e r v e r > . . . . . . . . . . . . . . . . < a t t r i b u t en a m e = " C a c h e M o d e " > R E P L _ S Y N C < / a t t r i b u t e > . . . . . . . . . . . . . . . .

< a t t r i b u t en a m e = " E v i c t i o n P o l i c y C o n f i g " > < c o n f i g > < a t t r i b u t en a m e = " w a k e U p I n t e r v a l S e c o n d s " > 5 < / a t t r i b u t e > < r e g i o nn a m e = " / _ d e f a u l t _ " > < a t t r i b u t en a m e = " m a x N o d e s " > 5 0 0 0 < / a t t r i b u t e >
www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

< a t t r i b u t en a m e = " t i m e T o I d l e S e c o n d s " > 1 0 0 0 < / a t t r i b u t e >

18/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

< a t t r i b u t en a m e = " t i m e T o I d l e S e c o n d s " > 1 0 0 0 < / a t t r i b u t e > < / r e g i o n > < r e g i o nn a m e = " / o r g / h i b e r n a t e / a u c t i o n / m o d e l / C a t e g o r y " > < a t t r i b u t en a m e = " m a x N o d e s " > 5 0 0 < / a t t r i b u t e > < a t t r i b u t en a m e = " t i m e T o I d l e S e c o n d s " > 5 0 0 0 < / a t t r i b u t e > < / r e g i o n > < r e g i o nn a m e = " / o r g / h i b e r n a t e / a u c t i o n / m o d e l / B i d " > < a t t r i b u t en a m e = " m a x N o d e s " > 5 0 0 0 < / a t t r i b u t e > < a t t r i b u t en a m e = " t i m e T o I d l e S e c o n d s " > 1 8 0 0 < / a t t r i b u t e > < / r e g i o n > < / c o n f i g > < / a t t r i b u t e > . . . . . . . . . . . . . . . . < / s e r v e r >

Entrar en las interioridades de JBossCache queda ya fuera del mbito de este artculo. Para obtener ms informacin sobre el producto, podis acceder a su pgina web.

6. La cach de consultas
Para finalizar con este artculo sobre tcnicas de cach con Hibernate, voy a hablar un poco sobre la cach de consultas. La cach de consultas de Hibernate permite almacenar las consultas realizadas recientemente, de modo que si se vuelven a realizar posteriormente, se recuperen los resultados de un modo mucho ms gil. La cach de consultas es recomendable tan slo para aplicaciones que hagan realmente muchas consultas y, que adems estas consultas sea previsible que sean repetitivas. Si las operaciones de actualizacin, eliminacin e insercin de entidades, son tambin muy frecuentes, entonces puede que no valga la pena utilizar una cach de consultas, ya que Hibernate invalida automticamente en las sesiones todas las entidades que participen en dichas operaciones.

La cach de consultas de Hibernate no almacena directamente los objetos, si no que tan slo almacena sus claves. De este modo, se reduce considerablemente el consumo de memoria que de otra manera sera excesivo. Esto tiene como consecuencia el que aunque hayamos hecho una consulta y sta se haya almacenado en la cach de consultas, si repetimos la misma consulta varios minutos despus, es posible que aunque la consulta siga en la cach, los objetos ya no se encuentren ni en la cach de primer nivel, ni en la cach de segundo nivel, ya que stas dos ltimas tienen la responsabilidad de gestionar sus entidades, independientemente de lo que haya en la cach de consultas. Como consecuencia de esto extraemos que aunque una consulta est en la cach de consultas, ello no tiene por que evitar obligatoriamente que tengamos que acceder a la base de datos para recuperar los resultados.

Para habilitar la cach de segundo nivel es necesario introducir en el fichero de configuracin de Hibernate la siguiente lnea:

h i b e r n a t e . c a c h e . u s e _ q u e r y _ c a c h e = t r u e

Una vez habilitada la cach de consultas, la nica forma de aprovecharla es utilizar la interfaz Query, ya que cualquier otra forma de realizar una consulta es ignorada por Hibernate en cuanto al almacenamiento en la cach de consultas. Por ejemplo:

Q u e r yq u e r y U s u a r i o s=s e s s i o n . c r e a t e Q u e r y ( " f r o mU s e ruw h e r eu . r o l =: r o l " ) ; q u e r y U s u a r i o s . s e t S t r i n g ( " r o l " , " A d m i n i s t r a d o r " ) ;


www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

q u e r y U s u a r i o s . s e t C a c h e a b l e ( t r u e ) ;

19/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

q u e r y U s u a r i o s . s e t C a c h e a b l e ( t r u e ) ;

Nuevamente, y para finalizar, he de recomendar utilizar la cach de consutas slo cuando vayamos a realizar frecuentemente repetidos y similares accesos. El cdigo anterior, no tendra ningn sentido si no se consultasen repetidas veces los usuarios que pertenecen al rol Administrador, porque si no se van a volver a consultar, para qu hemos habilitado la cach de consultas y estamos almacenando esta ltima?

7. Para finalizar
Espero sinceramente que este artculo haya servido para demostrar que la gestin de las cachs en una aplicacin empresarial no es una tarea sencilla. No debemos limitarnos a aplicar un producto sin ms, o a confiar directamente en un framework , sin detenernos a analizar las interioridades de su funcionamiento. En el caso de Hibernate, espero que haya quedado claro que tener el conocimiento de la gestin que realiza sobre los diferentes niveles de cach, nos puede ayudar enormemente a mejorar el rendimiento de nuestras aplicaciones.

Los sistemas de cach pueden aumentar considerablemente el rendimiento de servidores y aplicaciones. An as, como hemos visto, no estn exentos de problemas. La actualizacin concurrente, las cachs distribuidas, las polticas de sincronizacin, etc., son retos que tendremos que afrontar para conseguir el mximo rendimiento en nuestro sistema, pero una vez superados, tendremos grandes garantas de xito.

Como hemos visto, en Hibernate, confiar ciegamente en la cach de primer nivel nos puede traer muchos problemas de concurrencia y rendimiento. Un uso adecuado de la cach de segundo nivel, puede solucionar todos estos problemas, aunque no debemos olvidar que en ocasionnes, cuando tratemos con aplicaciones simples, quizs no sea necesario llegar hasta este punto de detalle. Por otra parte, la cach de consultas de Hibernate, muchas veces desconocida, nos puede aportar un punto extra en el rendimiento de nuestras aplicaciones y servidores; pero nuevamente, no debemos olvidar tomar precauciones.

8. Para aprender ms
Introduccin a Hibernate, Francesc Ross, /articles.article.action?id=9

Manual de Hibernate, Hctor Surez Gonzlez, /articles.article.action?id=82

Gua del autoestopista a Hibernate, Aitor Garca traduciendo a Glen Smith, /articles.article.action?id=80

Hibernate in Action, Gaving King y Christian Bauer, http://www.manning.com/bauer

Documentacin oficial de Hibernate, http://www.hibernate.org/5.html

Sitio web de EHCache, http://ehcache.sourceforge.net

Sitio web de JBossCache, http://jboss.org/products/jbosscache

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

20/21

23/07/13

javaHispano - Noticias importadas del antiguo javaHispano.org - Cachs, concurrencia e Hibernate

Acerca del autor

Martn Prez Marin es Ingeniero Tcnico en Informtica de Sistemas por la Universidad de A Corua. Adems, es Sun Certified Java Programmer, Sun Certified Java Developer y Sun Certified Business Component Developer. Ahora mismo trabaja como Ingeniero de Software para la empresa DINSA Soluciones dentro del Complejo Hospitalario Universitario Juan Canalejo de A Corua. Entre otros proyectos, durante el ltimo ao ha estado desarrollando un servidor de repositorio de colaboracin utilizado en jLibraryy basado en Hibernate+EHCache.

www.javahispano.org/antiguo_javahispano_org/2005/1/18/caches-concurrencia-e-hibernate.html

21/21

Você também pode gostar