DevPath · Aprende a programar ESPTEN

Rendimiento, transacciones y NoSQL

Transacciones y ACID

El problema: operaciones a medias

Imagina una transferencia bancaria: restar 100 € de la cuenta A y sumarlos a la cuenta B. Son dos escrituras. ¿Qué pasa si el sistema falla entre las dos? El dinero desaparece: se restó de A pero nunca llegó a B.

Una transacción resuelve esto: agrupa varias operaciones en una unidad indivisible que se ejecuta entera o nada.

BEGIN, COMMIT, ROLLBACK

BEGIN;
  UPDATE cuentas SET saldo = saldo - 100 WHERE id = 'A';
  UPDATE cuentas SET saldo = saldo + 100 WHERE id = 'B';
COMMIT;   -- confirma: ambos cambios se hacen permanentes a la vez
BEGIN;
  UPDATE cuentas SET saldo = saldo - 100 WHERE id = 'A';
  -- algo va mal (saldo insuficiente, error de validación...)
ROLLBACK;  -- nada se guarda; el saldo de A queda intacto

ACID: las cuatro garantías

Las transacciones de una base de datos relacional cumplen ACID:

Niveles de aislamiento

El aislamiento perfecto es caro (obliga a serializar todo). Por eso SQL define niveles que equilibran corrección y rendimiento, de menor a mayor garantía:

Nivel Permite Riesgo que evita
Read Uncommitted leer cambios no confirmados (dirty reads) el menos seguro
Read Committed solo leer datos ya confirmados dirty reads
Repeatable Read relecturas consistentes dentro de la transacción non-repeatable reads
Serializable como si las transacciones fueran en serie phantom reads (el más seguro)

Cuanto más alto el nivel, más correcto pero menos concurrente (más bloqueos).

Condiciones de carrera (race conditions)

Sin aislamiento adecuado aparecen condiciones de carrera: dos transacciones concurrentes leen el mismo dato, lo modifican y una sobrescribe a la otra (lost update).

Ejemplo clásico — reservar la última entrada de un concierto:

  1. Transacción T1 lee entradas = 1.
  2. Transacción T2 lee entradas = 1 (¡aún no se ha confirmado nada!).
  3. T1 resta 1 → escribe 0.
  4. T2 resta 1 → escribe 0.

Resultado: se han vendido dos entradas pero solo había una. Las transacciones con el nivel de aislamiento adecuado (o un bloqueo explícito como SELECT ... FOR UPDATE) evitan este error.

Regla mental: una transacción debe ser correcta aunque otra se ejecute al mismo tiempo.

Ejemplos

Transacción confirmada con COMMIT (transferencia)

BEGIN;
  UPDATE pedidos SET estado = 'pagado' WHERE id = 2;
  UPDATE pedidos SET estado = 'pagado' WHERE id = 6;
COMMIT;

SELECT id, estado FROM pedidos WHERE id IN (2, 6) ORDER BY id;

ROLLBACK deshace los cambios de la transacción

BEGIN;
  UPDATE pedidos SET total = 0 WHERE id = 1;
ROLLBACK;

-- El total de id=1 sigue siendo el original (120.0)
SELECT id, total FROM pedidos WHERE id = 1;
Pon esto en práctica

DevPath es un curso práctico: aquí lees la teoría; en la app la pones en práctica con ejercicios que se ejecutan de verdad, sin conexión.

Empezar gratis en la app →
← Índices: acelerar las búsquedasNoSQL, el teorema CAP y el escalado →