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

Condiții de cursă

Înțelegerea modului în care funcționează o bază de date izolată pe un singur terminal este simplă, dar într-un scenariu web real vor exista sute de procese simultane „Node/PHP/Go” care trimit cereri. Aici se nasc faimoasele Condiții de cursă.

Problema „Actualizare pierdută”.

Actualizarea pierdută se întâmplă atunci când două fire citesc același rând vechi, fac niște calcule pe el pe serverul JavaScript și apoi trimit înapoi UPDATE pentru a salva rezultatul.

Scenariu:

  • Produsul #5 are 10 unități.
  • Utilizatorul A cumpără unul (Serverul A citește „10”, calculează „9”).
  • În aceeași milisecundă utilizatorul B cumpără altul înainte ca achiziția lui A să termine faza COMMIT.
  • Serverul B citește și „10”, calculează „9”.
  • Serverul A trimite UPDATE: set quantity = 9.
  • Serverul B trimite UPDATE: set quantity = 9.
  • Ambii cred că au scăzut numărul cu succes. Dar valoarea finală ar fi trebuit să fie de 8 unități! O achiziție fantomă a fost pierdută.

Soluție fără tranzacții grele (Actualizări atomice)

În loc să citiți și să faceți calcule afară, lăsați DB să o facă în timpul actualizării în sine, care este întotdeauna sincronizată.

SQL
-- Wrong
UPDATE products SET stock = 9 WHERE id = 5;

-- Correct and natively thread-safe
UPDATE products SET stock = stock - 1 WHERE id = 5;

Mai multe anomalii extreme (Dirty Reads & Phantoms)

  • Dirty Read: ați citit datele temporare ale altcuiva care nu au fost încă confirmate de un COMMIT (și ar putea trece brusc la ROLLBACK, invalidându-vă).
  • Citituri fantomă: Faceți o scanare a tuturor rândurilor, între timp un al doilea thread inserează o nouă înregistrare, apoi faceți o altă scanare în timpul aceleiași tranzacții și descoperiți ca magic că noua înregistrare a apărut de nicăieri, compromițând rapoartele financiare calculate.
Exercițiu#sql.m11.l2.e1
Încercări: 0Se încarcă…

Modificați atomic înregistrarea clienților pentru o manipulare mică a șirurilor.\nSă presupunem că vrem să redenumim „orașul” unui client pentru a adăuga un asterisc. \nFă-o prin valorificarea propriei rânduri pentru cea cu id=10, concatenând „- Old” la valoarea curentă (Utilizați postgres' || conducta de concatenare).

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

UPDATE clienții SET oraș = oraș || ' - Vechi' WHERE id = 10;

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

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

O actualizare clasică pierdută are loc în plăți și prețuri.\nSă presupunem că există o creștere de urgență a prețului: creșteți „unit_price” din tabelul „order_items” (pentru produsul 1 din comanda 1) cu 20% atomic, adică înmulțindu-l * 1,2 în loc să inserați manual noul preț JS precomp.

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

UPDATE articole_comandă SET preț_unitate = preț_unitate * 1.2 WHERE cod_comandă = 1 ȘI cod_produs = 1;

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