Passer au contenu principal
eLearner.app
Module 11 · Leçon 3 sur 443/57 dans le cours~15 min
Leçons du module (3/4)

Verrouillage et SELECT FOR UPDATE

Comprendre comment fonctionne une base de données de manière isolée sur un seul terminal est simple... non attendez, je l'ai déjà dit ! Parfois, les « mises à jour atomiques » (par exemple stock = stock - 1) ne suffisent pas.

Imaginons que vous deviez vérifier si l'utilisateur dispose d'un solde suffisant pour acheter des produits d'une valeur de 50 € :

  1. SELECT balance FROM users WHERE id = 1;
  2. JS : Si le solde est < 50, bloquez-les !
  3. Sinon, procédez au paiement...
  4. Exécutez la déduction ! (Et ici vous trouvez un utilisateur qui, en validant le chèque à l'étape 2, en a lancé un autre parallèle qui a brûlé son compte et maintenant vous descendez en dessous de zéro !).

FOR UPDATE Verrouillage explicite

L'ajout de FOR UPDATE à la fin d'un SELECT transforme comme par magie votre lecture innocente en un verrou scellé contre d'autres clients jusqu'au prochain COMMIT. Personne d'autre ne pourra effectuer une MISE À JOUR (ou des lectures de mise à jour simultanées) sur ces lignes spécifiques sélectionnées jusqu'à ce que vous décidiez quoi en faire !

SQL
BEGIN;

-- If id=3 had already been locked by T1, T2 will sit in perpetual loading on this "read"
-- frozen in place, before receiving the raw data, until T1 runs COMMIT freeing everyone!
SELECT balance
FROM users
WHERE id = 3
FOR UPDATE;

-- at this point our Node JS instance lives with the guarantee, knowing for certain that NO ONE ELSE in the world has
-- manipulated (nor been able to read with intent to alter) that balance while we complete the application logic

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

COMMIT;

Il existe même FOR SHARE qui permet aux autres lecteurs de continuer à lire (s'ils sont sur un verrou de base) sans bloquer les lectures d'état, mais en bloquant toute mise à jour ultérieure possible jusqu'à ce que vous vous engagez. Cependant, pour les modifications, utilisez toujours la force brute de la clause de verrouillage exclusif :

Essayez-le vous-même

Exercice#sql.m11.l3.e1
Tentatives : 0Chargement…

Ouvrez une transaction normale à durée limitée et verrouillez exclusivement la lecture de l'ID de produit = 5. Exécutez une requête en deux étapes : 1. Ouvrez la transaction (BEGIN) 2. Interrogez « produits » en récupérant la ligne complète pour l'ID 5 avec la garantie que personne ne la modifiera, en attachant l'option « POUR MISE À JOUR ».

Chargement de l'éditeur…
Afficher l'indice

BEGIN ;, nouvelle ligne, SELECT * FROM products WHERE id = 5 FOR UPDATE ;

Solution disponible après 3 tentatives

Exercice#sql.m11.l3.e2
Tentatives : 0Chargement…

Parfois, la transaction se bloque pendant des périodes désastreuses (impasse ou attentes infinies). Pour éviter cela, vous pouvez ordonner à la base de données d'abandonner dès que la ligne n'est pas immédiatement disponible : en utilisant NOWAIT à la fin, ce qui provoquerait le rebond d'une exception du serveur SQL vers JS pour être interceptée sans se bloquer définitivement. Exécutez un SELECT pour la catégorie ID=1 et utilisez FOR UPDATE mais en appliquant NOWAIT.

Chargement de l'éditeur…
Afficher l'indice

SELECT * FROM catégories WHERE id = 1 FOR UPDATE NOWAIT ;

Solution disponible après 3 tentatives