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:
SELECT balance FROM users WHERE id = 1;- JS: Dacă soldul este < 50, blocați-le!
- În caz contrar, continuați cu plata...
- 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!
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
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”.
Afișează indiciu
BEGIN;, newline, SELECT * FROM produse WHERE id = 5 PENTRU UPDATE;
Soluție disponibilă după 3 încercări
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.
Afișează indiciu
SELECTAȚI * DIN categoriile UNDE id = 1 PENTRU UPDATE AȘTEPTĂ ACUM;
Soluție disponibilă după 3 încercări