CodeGym /Cursos /SQL SELF /Limitaciones y posibles problemas al trabajar con transac...

Limitaciones y posibles problemas al trabajar con transacciones: LOCK, DEADLOCK

SQL SELF
Nivel 54, Lección 1
Disponible

Las transacciones en PostgreSQL garantizan el aislamiento — una de las características clave de ACID. Para esto, el sistema usa locks. Un lock es un mecanismo que asegura que varias transacciones no "peleen" por el mismo registro o tabla. Imagínate una cola en el súper: en la caja solo atienden a una persona a la vez. Los locks funcionan más o menos igual, solo que controlan el acceso a los datos en las tablas.

Tipos principales de locks

En PostgreSQL hay varios tipos de locks, algunos de los cuales quizás ya viste en EXPLAIN ANALYZE:

  1. ROW EXCLUSIVE (lock exclusivo de fila) — aparece cuando se modifican datos en una fila. Es el tipo de lock más común, por ejemplo, cuando haces un INSERT, UPDATE o DELETE.
  2. SHARE (lock compartido) — se usa para operaciones que garantizan que los datos no cambien mientras se ejecuta la consulta, por ejemplo, con SELECT ... FOR SHARE.
  3. EXCLUSIVE (lock exclusivo) — impide cualquier otra operación sobre los datos, excepto lectura.

Podríamos decir que los locks son como perros guardianes que cuidan nuestros datos.

Problemas con los locks: ¿qué puede salir mal?

Ahora que entendemos qué son los locks, veamos qué problemas pueden causar. El más grande es el problema de los deadlocks. Un deadlock es una situación donde dos (o más) transacciones se esperan mutuamente, haciendo que todo se quede atascado en un bucle infinito. Normalmente pasa así:

  1. La transacción 1 bloquea la fila A y quiere acceder a la fila B.
  2. La transacción 2 bloquea la fila B y quiere acceder a la fila A.
  3. Como ambas filas están bloqueadas, ninguna transacción puede terminar.

Ejemplo de deadlock

Para sentir el dolor de un programador que se topa con un deadlock, imagina este escenario:

-- Transacción 1
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;

-- Al mismo tiempo
-- Transacción 2
BEGIN;
UPDATE accounts SET balance = balance + 200 WHERE id = 2;

-- Transacción 1 intenta bloquear id = 2
UPDATE accounts SET balance = balance - 100 WHERE id = 2;

-- Transacción 2 intenta bloquear id = 1
UPDATE accounts SET balance = balance + 200 WHERE id = 1;

¡Voilà! Cada transacción está bloqueada por la otra. PostgreSQL tarde o temprano lo detecta y lanza un error de deadlock.

¿Cómo evitar los deadlocks?

  1. Orden secuencial de acceso a los datos. Intenta siempre acceder a los datos en el mismo orden. Si la transacción 1 primero bloquea la fila A y luego la B, la transacción 2 debe seguir la misma lógica: primero la fila A, luego la B.
-- Orden correcto de acceso
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 200 WHERE id = 2;
COMMIT;
  1. Minimiza la duración de las transacciones. Las transacciones largas aumentan la probabilidad de deadlock. Termina las transacciones lo más rápido posible: BEGIN, haz todos los cambios necesarios y enseguida COMMIT.

  2. Usa niveles de aislamiento de transacciones. Si tu tarea no necesita un aislamiento de datos muy estricto, considera usar el nivel de aislamiento READ COMMITTED. Esto ayuda a evitar algunos locks.

Timeouts y diagnóstico de locks

Existen herramientas para diagnosticar y prevenir locks en PostgreSQL. Por ejemplo, puedes establecer un tiempo máximo de espera para un lock:

SET lock_timeout = '5s';  -- Ponemos el timeout de lock en 5 segundos

Si el lock se mantiene más tiempo del indicado, la transacción se cancela, evitando así un atasco total.

Monitorizando locks en PostgreSQL

Uno de los comandos útiles para monitorizar locks es pg_locks. Muestra todos los locks activos en el sistema:

SELECT * FROM pg_locks;

Puedes ver qué transacciones tienen locks y cuáles están esperando que se liberen. Es especialmente útil para depurar deadlocks.

LOCK y bloqueo manual de objetos

Si necesitas gestionar los locks manualmente, usa el comando LOCK:

LOCK TABLE orders IN ACCESS EXCLUSIVE MODE;

Ojo: ACCESS EXCLUSIVE es el lock más fuerte, impide cualquier otra operación sobre la tabla, incluso SELECT. Úsalo solo en casos especiales (por ejemplo, cambios en la estructura de la tabla).

Para bloquear filas individuales al modificarlas, usa SELECT ... FOR UPDATE:

SELECT * FROM accounts WHERE id = 1 FOR UPDATE;

Esto garantiza que otras transacciones no puedan modificar esas filas hasta que termine tu transacción.

¿Qué hay que recordar sobre los locks?

Los locks no son el enemigo, son una herramienta. Ayudan a mantener la integridad de los datos, pero hay que usarlos con cuidado. Aquí van unos consejos clave:

  1. No empieces una transacción si no tienes claro para qué la necesitas.
  2. Termina la transacción lo antes posible.
  3. Evita acceder a los datos en diferente orden.
  4. Usa el nivel de aislamiento que mejor se adapte a tu caso.

¡Ahora ya estás listo para trabajar con locks y deadlocks! Que pg_locks y la sabiduría de ACID te acompañen.

2
Tarea
SQL SELF, nivel 54, lección 1
Bloqueada
Creación y visualización de bloqueos
Creación y visualización de bloqueos
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION