Bună ziua tuturor. Vladislav Rodin la datorie. În prezent, sunt conducătorul cursului „Arhitect de înaltă încărcare” la OTUS și predau de asemenea cursuri dedicate arhitecturii software-ului.
Pe lângă predare, după cum ați observat, mă ocup de scrierea de materiale originale pentru blogul OTUS pe Habr și vreau să dedic articolul de astăzi lansării cursului , pentru care înscrierile sunt deschise chiar acum.

Introducere
În Am discutat despre cum tranzacțiile în bazele de date servesc pentru a rezolva două sarcini: asigurarea rezilienței și accesul la date într-un mediu concurențial. Pentru a îndeplini aceste sarcini complet, o tranzacție trebuie să aibă proprietățile ACID. Astăzi vom vorbi în detaliu despre litera I (izolare) din această abreviere.
Izolare
Izolarea adresează problema accesului la date într-un mediu concurențial, oferind efectiv protecție împotriva condițiilor de competiție. În ideal, izolarea înseamnă serializare, adică proprietatea care asigură că rezultatul execuției tranzacțiilor paralele este același cu cel dacă ar fi fost executate secvențial. Problema principală a acestei proprietăți este că este foarte greu de asigurat din punct de vedere tehnic și, ca urmare, afectează semnificativ performanța sistemului. De aceea, izolarea este adesea slăbită, asumându-și riscurile apariției unor anomalii despre care vom discuta mai jos. Posibilitatea apariției anumitor anomalii caracterizează exact nivelul de izolare al tranzacțiilor.
Cele mai cunoscute anomalii sunt: lectura murdară, lectura non-repetabilă, lectura fantomă, dar, de fapt, există încă 5: scrierea murdară, actualizarea pierdută a cursorului, actualizarea pierdută, citirea skew, scrierea skew.
Scrierea murdară
Esenta anomaliilor constă în faptul că tranzacțiile pot suprascrie date necomise.

Această anomalie este periculoasă nu doar pentru că datele pot intra în conflict după confirmarea ambelor tranzacții (așa cum se vede în imagine), ci și pentru că afectează atomicitatea: deoarece vom permite suprascrierea datelor necomise, nu este clar cum să revenim asupra unei tranzacții fără a afecta alta.
Anomalia se repară destul de simplu: punem un blocaj pe scriere înainte de a începe scrierea, interzicând altor tranzacții să schimbe înregistrarea până când blocajul este ridicat.
Lectura murdară
Lectura murdară înseamnă citirea unor date necomise.

Problemele apar atunci când este necesar să se realizeze acțiuni sau să se ia decizii pe baza unui eșantion.
Pentru a corecta anomalia, se poate aplica o blocare de citire, dar asta va afecta semnificativ performanța. Mult mai simplu este să spunem că, pentru rollback-ul tranzacției, starea inițială a datelor (înainte de începutul scrierii) trebuie să fie neapărat păstrată în sistem. De ce să nu citim de acolo? Este suficient de ieftin, așa că majoritatea bazelor de date dezactivează citirea murdară în mod implicit.
Actualizare pierdută
Actualizare pierdută înseamnă actualizări pierdute, iar traducerea reflectă destul de bine esența problemei:

Practically, the result of transaction T2 has been canceled. This situation is corrected by explicit or implicit record locks. That is, we either simply update the record, which causes an implicit lock, or we perform select for update, causing both read and write locks to occur. Note that this operation is quite dangerous: by our 'innocent' reading, we block other readings. Some databases offer a safer select for share, allowing data to be read but not modified.
Actualizarea pierdută a cursorului
Pentru un control mai detaliat, bazele de date pot oferi alte instrumente, cum ar fi cursorii. Un cursor este o structură care conține un set de rânduri și permite iterarea prin ele. declare cursor_name for select_statement. Conținutul cursorului este descris de select.
De ce este nevoie de un cursor? Problema este că unele baze de date oferă blocări pentru toate înregistrările selectate de select (stabilitate în citire), sau doar pentru înregistrarea pe care se află în acel moment cursorul (stabilitate a cursorului). La stabilitatea cursorului se realizează o blocare scurtă, ceea ce permite reducerea numărului de blocări în cazul în care iterăm printr-un eșantion mare de date. De aceea, anomalia actualizării pierdute este evidențiată pentru cursor separat.
Citire non-repetabilă
Citirea non-repetabilă constă în faptul că, în timpul executării tranzacției noastre, 2 citiri succesive ale aceleași înregistrări vor duce la obținerea unor rezultate diferite, deoarece o altă tranzacție a intervenit între aceste două citiri, a modificat datele noastre și a fost confirmată.

De ce este aceasta o problemă? Imaginați-vă că scopul tranzacției T2 din imagine este de a selecta toate produsele a căror preț este mai mic de 150 u.e. Cineva a actualizat prețul la 200 u.e. Astfel, filtrul stabilit nu a funcționat.
Aceste anomalii încetează să apară prin adăugarea de blocări în două etape sau prin utilizarea mecanismului MVCC, despre care dorim să discutăm separat.
Citire fantomă
Citirea fantomă se referă la citirea datelor care au fost adăugate de o altă tranzacție.

Ca exemplu, putem observa selecția incorectă a celui mai ieftin produs în cazul apariției acestei anomalii.
Eliminarea citirilor fantomă este deja destul de complicată. O simplă blocare nu este suficientă, deoarece nu putem bloca ceea ce nu există încă. Sistemele 2PL folosesc blocări predictive, în timp ce sistemele MVCC rețin tranzacțiile care pot fi afectate de inserții. Atât primul, cât și al doilea mecanism sunt destul de greoaie.
Citire distorsionată
Citirea distorsionată apare atunci când lucrăm cu mai multe tabele, ale căror conținuturi ar trebui să se schimbe în mod coerent.
Să presupunem că avem tabele care reprezintă postări și metainformațiile acestora:

O tranzacție citește din tabele, alta le modifică:

Ca rezultat al executării tranzacției T1, postarea cu titlul = Good, iar updated_by = T2, ceea ce reprezintă o anumită incongruență.
De fapt, aceasta este o citire non-repetabilă, dar în cadrul mai multor tabele.
Pentru a remedia situația, T1 poate impune blocări asupra tuturor rândurilor pe care le va citi, ceea ce nu va permite tranzacției T2 să modifice informația. În cazul MVCC, tranzacția T2 va fi anulată. Protecția împotriva acestei anomalii poate deveni importantă dacă folosim cursori.
Scriere distorsionată
Această anomalie este de asemenea mai ușor de explicat prin exemplu: să presupunem că în sistemul nostru trebuie să fie cel puțin un doctor de gardă, dar ambii doctori au decis să-și anuleze serviciul:


Anomalia a dus la faptul că niciunul dintre doctori nu va veni la serviciu. De ce s-a întâmplat asta? Pentru că tranzacția a verificat o condiție care ar putea fi afectată de o altă tranzacție, iar din cauza izolării nu am văzut această modificare.
Este aceeași citire non-repetabilă. Ca variantă, selectările pot impune blocări asupra acestor înregistrări.
Scrierea skew și citirea skew sunt combinații ale anomaliilor anterioare. Putem considera scrierea skew, care este în esență o citire fantomă. Să luăm în considerare un tabel în care sunt numele angajaților, salariul lor și proiectul pe care lucrează:


În cele din urmă, obținem următoarea imagine: fiecare manager credea că modificarea sa nu va duce la depășirea bugetului, așa că au făcut modificări de personal, care au dus la un surplus total.
Cauza problemei este exact aceeași ca și în cazul citirii fantome.
Conclusions
Slăbirea nivelului de izolare a tranzacțiilor în baza de date este un compromis între securitate și performanță, iar alegerea acestui nivel ar trebui să se facă în funcție de riscurile potențiale pentru afacere în cazul apariției anumitor anomalii.
Sursa: habr.com
