Salt la conținutul principal
Modulul 11 · Lecția 3 din 443/57 în curs~15 min
Lecții din modul (3/4)

Blocare și SELECTARE PENTRU ACTUALIZARE

Înțelegerea modului în care funcționează o bază de date izolată pe un singur terminal este simplă... nu stai, am spus deja asta! Uneori, „Actualizările atomice” (de exemplu, stock = stock - 1) nu sunt suficiente.

Să ne imaginăm că trebuie să verificați dacă utilizatorul are suficient sold pentru a cumpăra lucruri în valoare de 50 EUR:

  1. SELECT balance FROM users WHERE id = 1;
  2. JS: Dacă soldul este < 50, blocați-le!
  3. În caz contrar, continuați cu plata...
  4. Rulați deducerea! (Și aici găsești un utilizator care, în timp ce valida verificarea la pasul 2, a mai tras un paralel care i-a ars contul și acum cobori sub zero!).

PENTRU ACTUALIZARE Blocare explicită

Adăugarea FOR UPDATE la sfârșitul unui SELECT transformă magic citirea ta nevinovată într-un Lacăt sigilat împotriva altor clienți până la următorul COMMIT. Nimeni altcineva nu va putea face UPDATE (sau citiri simultane pentru actualizare) pe acele rânduri specifice selectate până când decideți ce să faceți cu ele!

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;

Există chiar și FOR SHARE care le permite altor cititori să continue să citească (dacă sunt pe o bază de blocare) fără a bloca citirile de stare, dar blocând orice actualizare ulterioară posibilă până când te angajezi. Pentru modificări, totuși, folosiți întotdeauna forța brută a clauzei de blocare exclusive:

Încercați-l singur

Exercițiu#sql.m11.l3.e1
Încercări: 0Se încarcă…

Deschideți o tranzacție normală cu interval de timp și blocați exclusiv citirea ID-ului produsului=5.\nRulați o interogare în doi pași:\n1. Deschideți tranzacția (ÎNCEPE)\n2. Interogați „produse” care preia rândul complet pentru ID-ul 5, cu garanția că nimeni nu îl va modifica, atașând opțiunea „PENTRU ACTUALIZARE”.

Se încarcă editorul...
Afișează indiciu

BEGIN;, newline, SELECT * FROM produse WHERE id = 5 PENTRU UPDATE;

Soluție disponibilă după 3 încercări

Exercițiu#sql.m11.l3.e2
Încercări: 0Se încarcă…

Uneori, tranzacția se blochează pentru perioade dezastruoase (deadlock sau așteptări infinite).\nPentru a preveni acest lucru, puteți comanda bazei de date să renunțe la momentul în care rândul nu este disponibil imediat: folosind NOWAIT la sfârșit, ceea ce ar face ca o excepție de server SQL să revină la JS pentru a fi încercată fără să se blocheze pentru totdeauna.\nRulați un ID = SELECT pentru categoria UPDITWAIT = SELECT.

Se încarcă editorul...
Afișează indiciu

SELECTAȚI * DIN categoriile UNDE id = 1 PENTRU UPDATE AȘTEPTĂ ACUM;

Soluție disponibilă după 3 încercări