Saltar al contenido principal
eLearner.app
Módulo 11 · Lección 3 de 443/57 en el curso~15 min
Lecciones del módulo (3/4)

Bloqueo y SELECT FOR UPDATE

Entender cómo funciona una base de datos de forma aislada en un único terminal es sencillo… ¡no, espera, ya lo dije! A veces, las "Actualizaciones atómicas" (por ejemplo, stock = stock - 1) no son suficientes.

Imaginemos que necesitas comprobar si el usuario tiene saldo suficiente para comprar 50 € en cosas:

  1. SELECT balance FROM users WHERE id = 1;
  2. JS: Si el saldo es < 50, ¡bloquealo!
  3. De lo contrario, continúe con el pago...
  4. ¡Haz la deducción! (¡Y aquí te encuentras con un usuario que, mientras validaba el cheque en el punto 2, lanzó otro paralelo que quemó su cuenta y ahora cae por debajo de cero!).

PARA ACTUALIZAR Bloqueo explícito

Agregar FOR UPDATE al final de un SELECT transforma mágicamente su lectura inocente en un bloqueo para otros clientes hasta el próximo COMMIT. ¡Nadie más podrá ACTUALIZAR (o leer para obtener actualizaciones simultáneas) en esas líneas seleccionadas específicas hasta que usted decida qué hacer con ellas!

SQL
BEGIN;

-- Se id=3 era già stato bloccato da T1, T2 rimarrà in perenne caricamento su questa "lettura"
-- fermo immobile, prima di ricevere il dato grezzo, finchè T1 non esegue il COMMIT liberando tutti!
SELECT balance
FROM users
WHERE id = 3
FOR UPDATE;

-- a questo punto la nostra istanza JS Node vive garantita sapendo per certo che NESSUN ALTRO al mondo ha
-- manipolato (nè potuto leggere a scopo di alterare) quel saldo mentre completiamo la logica applicativa

UPDATE users SET balance = balance - 50 WHERE id = 3;

COMMIT;

Incluso existe el FOR SHARE que deja la posibilidad a otros lectores de continuar leyendo (si está en bloqueo base) sin impedir la lectura del estado pero bloqueando cualquier posible actualización posterior hasta que usted se comprometa. Sin embargo, para modificaciones, utilice siempre la fuerza bruta de la cláusula de bloqueo exclusivo:

Pruébalo tú mismo

Ejercicio#sql.m11.l3.e1
Intentos: 0Cargando...

Abra una transacción en tiempo normal y bloquee exclusivamente la lectura del ID del producto = 5. Ejecute una consulta de dos pasos: 1. Abrir transacción (COMENZAR) 2. Consulte los 'productos' buscando en toda la fila el ID 5 con la garantía de que nadie lo altere y luego adjunte la opción 'PARA ACTUALIZACIÓN'.

Cargando editor...
Mostrar pista

COMENZAR;, Regresar, SELECCIONAR * DE productos DONDE id = 5 PARA ACTUALIZAR;

Solución disponible después de 3 intentos

Ejercicio#sql.m11.l3.e2
Intentos: 0Cargando...

A veces, la transacción se bloquea durante períodos desastrosos (punto muerto o esperas interminables). Para evitar esto, puede ordenar a la base de datos que se dé por vencida cuando la línea no se encuentre inmediatamente: usando NOWAIT al cerrar, lo que por lo tanto provocaría que la excepción del servidor SQL se envíe de vuelta al JS para intentar capturarla sin colgarla de por vida. Haga un SELECT para la categoría ID=1 y use FOR UPDATE pero imponiendo NOWAIT.

Cargando editor...
Mostrar pista

SELECCIONE * DE categorías DONDE id = 1 PARA ACTUALIZAR AHORA ESPERE;

Solución disponible después de 3 intentos