Definición de transacción: conjunto de operaciones (de lectura y actualización) que se ejecutan como una unidad indivisible de trabajo. La transacción finaliza su ejecución confirmando o cancelando los cambios realizados sobre la base de datos.
Toda transacción debe cumplir las cuatro propiedades conocidas como ACID:
- Atomicidad: unidad atómica, indivisible de ejecución. O bien se ejecuta entera o no se ejecuta nada.
- Consistencia: debe de preservar la consistencia de la base de datos.
- Aislamiento: una transacción no puede verse afectada por la ejecución de otra.
- Definitividad: los resultados de una transacción llevada a cavo exitosamente tienen que ser definitivos en la base de datos.
Los sistemas gestores de bases de datos pueden reaccionar de dos maneras ante transacciones concurrentes, o bien cancelan las transacciones problemáticas, o bien las bloquean hasta que puedan ejecutarse.
Estructura utilizada desde SQL:1999:
START TRANSACTION [READ ONLY | READ WRITE] <- modo de acceso INSOLATION LEVEL [UNCOMMITTED | READ COMMITTED | REPEATABLE | SERIALIZABLE] <- nivel de aislamiento -- Recuperar datos [...] -- Insertar datos [...] -- Actualizar datos [...] [COMMIT | ROLLBACK | WORK ]
Recomendaciones para implementación de transacciones:
- Las transacciones sean lo más cortas posibles, no incluir nada superfluo que pueda ir fuera de la transacción.
- No utilizar llamadas al usuario para la solicitud de datos como PROMPT.
- No dejar transacciones abiertas, es decir sin su correspondiente commit o rollback.
- Establecer el nivel de aislamiento correcto para cada transacción.
Tipos de interferencias:
Actualización perdida (lectura sucia):
Transacción T1 (Finaliza con 70€ de saldo) | Transacción T2 (Finaliza con 50€ de saldo) |
saldo := consulta_saldo(cuenta) >> Lee 100€ | |
saldo := consulta_saldo(cuenta) >> Lee 100€ | |
escribir_saldo(cuenta, saldo – 30) >> Actualiza a 70€ | |
escribir_saldo(cuenta, saldo – 50) >> Actualiza a 50€ | |
COMMIT >> Finaliza con 70€ | |
COMMIT >> Finaliza con 50€ |
- La transacción T1 inserta una fila en una tabla.
- La transacción T2 lee la fila nueva.
- La transacción T1 efectúa una retrotracción.
Lectura no confirmada (lectura sucia):
Transacción T1 (Finaliza con 100€ de saldo) | Transacción T2 (Finaliza con 70€ de saldo) |
saldo := consulta_saldo(cuenta) >> Lee 100€ | |
escribir_saldo(cuenta1, saldo – 30) >> Actualiza a 70€ | |
saldo := consulta_saldo(cuenta) >> Lee 70€ | |
COMMIT >> Finaliza con 70€ | |
ROLLBACK >> Finaliza con 100€ |
- La transacción T1 inserta una fila en una tabla.
- La transacción T2 lee la fila nueva.
- La transacción T1 efectúa una retrotracción.
Lectura no replicable:
Transacción T1 (Lee 100 y finaliza con 50€ de saldo) | Transacción T2 (Finaliza con 50€ de saldo) |
saldo := consulta_saldo(cuenta) >> Lee 100€ | |
saldo := consulta_saldo(cuenta) >> Lee 100€ | |
escribir_saldo(cuenta1, saldo – 50) >> Actualiza a 50€ | |
saldo := consulta_saldo(cuenta) >> Lee 50€ | |
COMMIT >> Finaliza con 50€ | |
COMMIT >> Finaliza con 50€ |
- La transacción T1 lee una fila.
- La transacción T2 cambia la fila.
- La transacción T1 lee la misma fila por segunda vez y obtiene los resultados nuevos.
Lectura fantasma (analisis inconsistente):
Transacción T1 (Consultar saldos) | Transacción T2 (Realizar transferencia entre cuentas) |
saldo1 := consulta_saldo(cuenta1) >> Lee 100€ | |
saldo2 := consulta_saldo(cuenta2) >> Lee 100€ | |
escribir_saldo(cuenta2, saldo – 30) >> Actualiza a 70€ | |
saldo1 := consulta_saldo(cuenta1) >> Lee 100€ | |
saldo1 := consulta_saldo(cuenta1) >> Lee 100€ | |
escribir_saldo(cuenta1, saldo + 30) >> Actualiza a 130€ | |
COMMIT >> Finaliza con 100€ y 100€ | |
COMMIT >> Finaliza con 70€ y 130€ |
- La transacción T1 lee todas las filas que satisfacen una cláusula WHERE de una consulta SQL.
- La transacción T2 inserta una fila adicional que satisface la cláusula WHERE.
- La transacción T1 vuelve a evaluar la condición WHERE y recoge la fila adicional.
Niveles de aislamiento:
- READ_UNCOMMITTED
- Este nivel permite que las transacciones vean los cambios no comprometidos en los datos. En este nivel son posibles todas las anomalías de base de datos.
- READ_COMMITTED
- Este nivel protege parcialmente las lecturas e impide que la transacción lea los datos actualizados por otra transacción que todavía no se hayan confirmado. Impide las lecturas sucias.
- REPEATABLE_READ
- Este nivel impide que otra transacción actualice un dato que haya leído la transacción hasta que esta no se acabe. Con esto se consigue que la transacción pueda volver a leer este dato sin riesgo de que lo hayan cambiado.
- SERIALIZABLE
- ste nivel ofrece un aislamiento total y evita cualquier tipo de interferencias, incluyendo los fantasmas. Esto implic que no solamente protege los datos que haya visto la transacción, sino también cualquier información de control que se haya utilizado para hacer búsquedas.
Se puede utilizar el método SET TRANSATION para cambiar el nivel de aislamiento de las transacciones para una conexión.
Fuente: IBM – Niveles de ailamiento de las transacciones
Interferencias y nivel de aislamiento adecuado
Actualización perdida | Lectura no confirmada | Lectura no replicable | Lectura fantasma | |
READ_UNCOMMITTED | Si | No | No | No |
READ_COMMITTED | Si | Si | No | No |
REPEATABLE_READ | Si | Si | Si | No |
SERIALIZABLE | Si | Si | Si | Si |
Si: se evita;
No: no se evita.
Me parece excelente la información presentada. Gracias por el aporte.