Transacciones en los sistemas gestores de base de datos

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:

  1. Atomicidad: unidad atómica, indivisible de ejecución. O bien se ejecuta entera o no se ejecuta nada.
  2. Consistencia: debe de preservar la consistencia de la base de datos.
  3. Aislamiento: una transacción no puede verse afectada por la ejecución de otra.
  4. 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€
  1. La transacción T1 inserta una fila en una tabla.
  2. La transacción T2 lee la fila nueva.
  3. 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€
  1. La transacción T1 inserta una fila en una tabla.
  2. La transacción T2 lee la fila nueva.
  3. 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€
  1. La transacción T1 lee una fila.
  2. La transacción T2 cambia la fila.
  3. 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€
  1. La transacción T1 lee todas las filas que satisfacen una cláusula WHERE de una consulta SQL.
  2. La transacción T2 inserta una fila adicional que satisface la cláusula WHERE.
  3. 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.

 

Otros artículos que pueden ser de interés:

Autor: Diego Calvo