Estrategias de concurrencia13 temas

Estrategias de concurrencia

Dos estrategias evitan las anomalías de concurrencia. Cada una responde a un balance distinto entre rendimiento y consistencia.


Bloqueo pesimista — prevenir el conflicto

Esta estrategia parte de una premisa: los conflictos entre transacciones son inevitables, por lo tanto deben prevenirse. El sistema bloquea el recurso antes de cualquier operación y garantiza acceso exclusivo mientras esté en uso. En concreto:

Aspecto Descripción
Acceso Exclusivo La primera transacción que pide el recurso obtiene el bloqueo. Las demás esperan hasta que se libere.
Integridad Garantizada El sistema evita cualquier anomalía garantizando que nadie más lea o modifique los datos hasta que la transacción termine con COMMIT o ROLLBACK.
Impacto operativo Gana la consistencia, pero se pierde fluidez: las transacciones se ejecutan una detrás de otra para evitar colisiones.
-- Sesión 1                      |  -- Sesión 2
BEGIN;                           |
SELECT stock FROM productos      |
  WHERE id = 1 FOR UPDATE;       |
-- T1 bloquea la fila            |
-- stock: 10                     |
                                 |  BEGIN;
                                 |  SELECT stock FROM productos
                                 |    WHERE id = 1 FOR UPDATE;
                                 |  -- T2 ESPERA hasta que T1 termine
UPDATE productos SET stock = 9   |
  WHERE id = 1;                  |
COMMIT;                          |
-- T1 libera el lock             |
                                 |  -- T2 obtiene el lock
                                 |  -- stock: 9
                                 |  UPDATE productos SET stock = 8
                                 |    WHERE id = 1;
                                 |  COMMIT;
-- Resultado final: stock = 8    |

Bloqueo optimista — detectar el conflicto

A diferencia del pesimista, este enfoque asume que los conflictos son poco comunes. Deja que todas las transacciones trabajen al mismo tiempo sin restricciones, y solo verifica si hubo cambios justo antes de guardar los datos.

El mecanismo más común es usar una columna de versión:

-- Tabla con columna de versión
CREATE TABLE productos (
  id      INTEGER PRIMARY KEY,
  stock   INTEGER NOT NULL,
  version INTEGER NOT NULL DEFAULT 1
);

-- Sesión 1                      |  -- Sesión 2
BEGIN;                           |
SELECT stock, version            |
  FROM productos WHERE id = 1;   |
-- stock: 10, version: 1         |
                                 |  BEGIN;
                                 |  SELECT stock, version
                                 |    FROM productos WHERE id = 1;
                                 |  -- stock: 10, version: 1
UPDATE productos                 |
  SET stock = 9, version = 2     |
  WHERE id = 1 AND version = 1;  |
-- filas afectadas: 1            |
-- éxito, el dato no cambió      |
COMMIT;                          |
                                 |  UPDATE productos
                                 |    SET stock = 9, version = 2
                                 |    WHERE id = 1 AND version = 1;
                                 |  -- filas afectadas: 0
                                 |  -- ERROR: el dato ya fue modificado
                                 |  -- T2 debe reintentar la operación

El truco está en el WHERE version = 1. Si la fila fue modificada por otra transacción, la versión ya no es 1 y el UPDATE no afecta ninguna fila. La aplicación detecta que no hubo filas actualizadas y sabe que debe reintentar.

Modelo mental:

Imagina que accedes a un recurso compartido sin reserva previa. Al intentar finalizar tu gestión, el sistema verifica si el estado del recurso cambió mientras lo usabas. Si alguien más lo modificó, tu operación es rechazada y debes reiniciar el proceso.


Comparación: pesimista vs optimista

Dimensión Pesimista Optimista
Enfoque Previene el conflicto antes de que ocurra Detecta el conflicto al momento de escribir
Ventaja Consistencia total Nunca hay datos inconsistentes. Si obtiene el lock, la transacción trabaja sin interferencias. Alta concurrencia Las transacciones no se bloquean entre sí mientras leen.
Desventaja Baja concurrencia Varias transacciones pueden bloquearse esperando recursos retenidos entre sí. Reintentos Si hay conflicto, hay que reintentar toda la transacción. En alta contención, los reintentos pueden ser peores que los locks.
Cuándo usarlo Alta contención — muchos usuarios modificando los mismos datos a la vez Baja contención — los conflictos son poco frecuentes
Cuándo evitarlo Operaciones lentas — mantener un lock mucho tiempo reduce el throughput Alta contención — los reintentos constantes degradan el rendimiento

Casos de uso

Situación Recomendación
Reserva de asientos en un vuelo (mucha gente, mismos asientos) Pesimista — evitar sobreventas
Editar un artículo en un CMS (baja probabilidad de conflicto) Optimista — mejor experiencia de usuario
Transferencia bancaria (dos cuentas, alta integridad) Pesimista — no puede haber reintentos
Likes en una red social (muchos, datos simples) Optimista — los reintentos son baratos

Errores frecuentes

Error: El bloqueo optimista no necesita locks

El bloqueo optimista también usa locks, pero muy breves. La diferencia está en la duración, no en la existencia.

Autoevaluación: Estrategias de Concurrencia

1/3