Database Isolation levels
ACID (Atomicidad, Consistencia, Aislamiento, Durabilidad) son un conjunto de propiedades que debe cumplir una base de datos al hacer una modificación. Las transacciones son una manera para garantizar estas propiedades.
Atomicidad: Las transacciones se ejecutan al 100% o no se ejecutan.
Consistencia: Se garantiza que los datos serán consistentes; ningún constraint definido debe ser violado.
Aislamiento (Isolation): Las transacciones deben tener la capacidad de ejecutarse de manera aislada unas de otras (dependiendo el nivel se aislan poco o mucho).
Durabilidad: Una vez guardados los datos (commit) deben mantenerse a salvo en algún medio.
A continuación se describen los problemas más comunes que se presentan cuando se ejecuta más de una transacción al mismo tiempo:
Descripción: T1 ve los cambios de T2 aunque T2 no haya hecho commit.
T1 lee x=0
T1 modifica x=10, y no hace commit por lo que x=0 en la base de datos.
T2 lee el valor modificado por T1, x=10 aunque T1 aún no haya hecho commit.
Peligros: Si T1 hace rollback, T2 se quedará con una lectura sucia (x=10)
Descripción: T1 ve los cambios a los que se les ha hecho commit en T2.
T1 lee x=0
T2 modifica x=10
T2 commit
T1 lee x=10
Peligros: T1 obtiene distintos valores de x (lectura no repetible) porque T2 modificó x e hizo commit. Esto puede llevar a inconsistencias.
Descripción: T1 obtiene N filas en un select pero en un posterior select puede obtener un número distinto de filas porque T2 insertó.
Ansi establece distintos niveles de aislamiento entre transacciones para solventar los problemas anteriormente descritos.
El nivel de isolación de una transacción determina qué datos puede ver la transacción cuando otras transacciones se ejecutan concurrentemente.
Hay que tener en cuenta que a mayor nivel de aislamiento es mayor la consistencia pero mayores son los sacrificios que se hacen con respecto a la concurrencia y al rendimiento.
En la práctica casi no se suele utilizar este nivel de aislamiento ya que es propenso a sufrir todos los problemas anteriormente descritos. En este nivel una transacción puede ver los resultados de transacciones que aún no han hecho commit. Podemos apreciar que en este nivel no existe aislamiento alguno entre transacciones.
TX A: start transaction;
TX B: set session transaction isolation level read uncommitted;
TX B: start transaction;
TX A: select * from test; -- val = 8
TX B: select * from test; -- val = 8
TX A: update test set val = val + 1; -- val = 9
TX B: select * from test; -- val = 9, dirty read
TX A: rollback;
TX B: select * from test; -- val = 8
TX B: commit;
Como se puede observar es posible que TX B vea los datos que fueron modificados por TX A (sin que haya hecho commit). Sin embargo si TX A hace rollback TX B se quedará con un dato incorrecto (lectura sucia).
Es el predeterminado para la mayoría de gestores de bases de datos relacionales. Supone que dentro de una transacción únicamente se pueden ver los cambios de las transacciones que hacen commit. Soluciona el problema de las lecturas sucias, pero no el de las lecturas no repetibles ni tampoco el de las lecturas fantasmas.
TX A: start transaction;
TX B: set session transaction isolation level read committed;
TX B: start transaction;
TX A: select * from test; -- val = 8
TX B: select * from test; -- val = 8
TX A: update test set val = val + 1; -- val = 9
TX B: select * from test; -- val = 8, No dirty read!
TX A: commit
TX B: select * from test; -- val = 9, commited read
Este nivel muestra cómo las lecturas sucias no son posibles. Los datos de TX A estarán disponibles en TX B hasta que TX A haga commit.
Garantiza que un valor obtenido será siempre el mismo dentro de la transacción aunque otras transacciones lo modifiquen y hagan commit. De esta forma se soluciona, además de las lecturas sucias, el problema de las lecturas no repetibles. Aunque en dicho nivel se siguen dando las lecturas fantasmas.
TX A: start transaction;
TX B: set session transaction isolation level repeatable read;
TX B: start transaction;
TX A: select * from test; -- val = 8
TX B: select * from test; -- val = 8
TX A: update test set val = val + 1; -- val = 9
TX B: select * from test; -- val = 8
TX A: commit
TX B: select * from test; -- val = 8, repeatable read!
TX B: commit;
TX B: select * from test; -- val = 9 (from tx A)
TX B ve siempre los mismos datos aunque TX A los modifique y haga commit, si TX A hizo commit TX B podrá verlos hasta que haga commit/rollback.
Si se ejecutan N transacciones serializables al mismo tiempo, se garantiza que se tendrá un comportamiento como si se ejecutara una después de otra. Este nivel de aislamiento es bastante problemático ya que es, con diferencia, el que más sacrifica en rendimiento y concurrencia.
TX A: start transaction;
TX B: set session transaction isolation level serializable;
TX B: start transaction;
TX A: select * from test; -- val = 8
TX A: update test set val = val + 1; -- val = 9
TX B: select * from test; -- LOCKED, NO OUTPUT
TX A: commit; -- Unlocked TX B
TX B: select * from test; -- val = 8 (repeatable read!)
TX B: commit;
TX B: select * from test; -- val = 9 (now we see TX A)
READ COMMITTED
es el isolation level default de PostgreSQL.REPEATABLE READ
.http://www.postgresql.org/docs/9.1/static/transaction-iso.html