martes, 15 de enero de 2008

Isolation Level en Hibernate

El isolation level o nivel de aislamiento es una de las configuraciones a nivel de base de datos que en ocasiones olvidamos establecer.
Podríamos decir que determina la forma en que los datos son "lockeados" o aislados de otros procesos mientras son accedidos, es decir, define como y cuando los cambios realizados por una transacción son visibles por otras operaciones concurrentes, influyendo en las siguientes anomalías:

Dirty Reads: Ocurre cuando una transacción lee datos modificados por otros procesos concurrentes que aún no han realizado commit, por eso se la denomina lectura sucia.

Nonrepeatable Reads: Ocurre cuando dentro de una transacción se lee el mismo registro mas de una vez y los datos obtenidos son diferentes, seguramente porque otro proceso concurrente los actualizó.

Phantom Reads: Ocurre cuando en una transacción ejecutamos el mismo query mas de una vez y obtenemos distintos resultados, por ejemplo si otra transacción agregó nuevos registros que satisfacen el criterio de búsqueda de la consulta.

El estándar ANSI/ISO SQL define cuatro niveles de aislamiento:

READ UNCOMMITTED: Permite que se produzcan Dirty Reads, Nonrepeatable Reads y PhantomReads. Es la opción menos restrictiva, recomendable si se manejan datos de solo lectura o muy pocas actualizaciones.

READ COMMITTED: Evita Dirty Reads, pero permite que se produzcan Nonrepeatable Reads y Phantom Reads.

REPEATABLE READ: Solo permite que se produzcan Phantom Reads.

SERIALIZABLE: Evita todos los casos anteriores, es la opción mas restrictiva.

Cada manejador de base de datos tiene su forma de configurar el isolation level a través de un administrador gráfico o por línea de comandos, por otro lado también es bueno aclarar que los productos difieren en los valores por defecto, por ejemplo en MySQL el default es REPEATABLE READ, en cambio en Oracle y SQL Server es READ COMMITTED.

Si usamos Hibernate como framework de persistencia podemos usar el property "hibernate.connection.isolation" en el hibernate.cfg.xml. Por ejemplo, para establecer la estrategia READ COMMITTED usaríamos:

<property name="hibernate.connection.isolation">2</property>

Los valores posibles son los mismos que define la interface java.sql.Connection:

1: READ UNCOMMITTED
2: READ COMMITTED
4: REPEATABLE READ
8: SERIALIZABLE

Tener en cuenta la aclaración de la documentación oficial : "... note that most databases do not support all isolation levels". Ante la duda siempre lo mejor es hacer una prueba ejecutando dos transacciones en paralelo para verificar que tenemos el comportamiento esperado.

Hasta el próximo post!

12 comentarios:

nando dijo...

Gracias jefe, me ha servido de mucha ayuda.

developmentJava dijo...

Muy , buena mi estimado me sirvio de mucho.

Saludos

David dijo...

Hola Federico.
Enhorabuena por el post.
Yo tengo una pregunta, si nosotros definimos en la bbdd el nivel de isolation, ¿para que sirve definir en hibernate el isolation level? ¿lo sobreescribe?

Gracias.

Federico Varela dijo...

Hola David, gracias por tu comentario.
Con respecto a tu consulta, siempre he configurado la propiedad "hibernate.connection.isolation" y he dejado por defecto el valor en la BD. Hibernate sobreescribe el valor, es útil hacerlo de esta manera cuando un aplicativo comparte una BD con otros sistemas y no queremos impactos cruzados. Saludos.

David dijo...

Gracias Federico.
Pero piensa en el escenario que en la BBDD tengamos un nivel alto de isolación y desde una aplicación A con hibernate configuramos uno más bajo, ¿cuál prevalece? Porque la otra aplicación B quizás se base en el hecho de que tenemos configurado el máximo nivel en BBDD...

Federico Varela dijo...

David, Hibernate sobreescribe el isolation al obtener la conexión del pool y luego la libera para que se recicle. Si hay una aplicación B que apunta a la misma base no hay efectos cruzados, dependerá de como esta última obtenga las conexiones, si usa JDBC "crudo" tendrá el valor por defecto configurado en la BD.
Saludos.

TECNOMOVIL dijo...

Buenas tardes Federico, antes que todo una felicitación por tu post. Disuclpa ojala me puedas ayudar, tengo un problema, hago un SELECT a una BD en informix y como es muy concurrida la BD a veces me devuelve una excepcion porque hay bloqueos por otras transacciones y no puede leer los datos, puse esto 1 en mi hibernate.cfg.xml y pense que asi ya no tendria problemas porque funcionaria como un dirty read y leeira lo que pudiera aunque la tabla estuviera bloqueda pero no me funciono, me sigue pasando lo mismo. No se si con esto resuelvo mi problema, o como lo podria resolver? De ante mano muchas gracias!! Saludos!!

Juan Carlos dijo...

Perdon, soy el del comentario de arriba, es que no salio una parte, lo que puse en mi archivo de configuracion de hibernate fue el hibernate.connection.isolation en 1.

Gracias!! Saludos!!

Federico Varela dijo...

Hola Juan Carlos, gracias por el comentario.

Sobre tu consulta, en realidad el problema no es de isolation level, sino de lock mode. Seguramente los otros procesos bloquean la tabla y no permiten la lectura de tu select.

Tendrías que investigar como realizan el locking los otros procesos.

Saludos

Federico

Juan Carlos dijo...

Gracias por la pronta respuesta Federico.

Ok, seguire investigando.

Saludos!!

Anónimo dijo...

Federico

Qué falta hace en nuestro sector gente capaz de explicar las cosas para que todo el mundo las entienda. Que sepas que tu hilo corre por todos mis usuarios cada vez que quiero explicarles exactamente qué es y cómo funciona el aislamiento.
Mil gracias.

Blog de Andrés Vadillo de la Fuente dijo...

Muy , pero que muy bueno me pase tres días con este problema siempre y aleatoria mente se reflejaban con mi motor Mysql datos de antiguas transacciones, vi este post y solucione mi problema y entendido. Gracias por todo muy muy buen post