Transaksionet
Transaksioni është një sekuencë e operacioneve mbi të dhëna që ka fillim dhe fund.
Transaksioni është ekzekutimi sequential i operacioneve të leximit dhe shkruajtjes. Fundi i një transaksioni mund të jetë ose shpëtimi i ndryshimeve (konfirmimi, commit) ose anullimi i ndryshimeve (rrezimi, rollback). Ndërsa i referohemi bazave të të dhënave, një transaksion përbëhet nga disa kërkesa që interpretohen si një kërkesë e vetme.
Transaksionet duhet të përmbushin pronat ACID.
Ato më të thella. Transaksioni ose realizohet plotësisht ose nuk realizohet fare.
Konsistenca. Në përfundim të transaksionit, kufizimet e vendosura mbi të dhënat nuk duhet të shkelet (për shembull, constraints në bazat e të dhënave). Konsistenca nënkupton që sistemi do të kalojë nga një gjendje e saktë në një tjetër gjendje të saktë.
Izolimi. Transaksionet që ekzekutohen paralelisht nuk duhet të ndikojnë tek njëra-tjetra, për shembull, të ndryshojnë të dhënat që përdor një transaksion tjetër. Rezultati i ekzekutimit të transaksioneve paralel duhet të jetë i tillë siç do të ishte nëse transaksionet do të ishin ekzekutuar njëra pas tjetrës.
Qëndrueshmëria. Pas konfirmimit, ndryshimet nuk duhet të humbasin.
Dita e transaksioneve
Dita ruan ndryshimet e bëra nga transaksionet, siguron thellësinë dhe qëndrueshmërinë e të dhënave në rast të një dështimi të sistemit.
Dita përmban vlerat që të dhënat kishin para dhe pas ndryshimit nga transaksioni. Strategjia e shkrimit përpara obligon shtimin në ditë të shënimeve mbi vlerat e mëparshme përpara fillimit dhe mbi vlerat përfundimtare pas përfundimit të transaksionit. Në rast të ndalimit të papritur të sistemit, baza e të dhënave lexon ditën në rend të kundërt dhe anullon ndryshimet e bëra nga transaksionet. Kur has një transaksion të ndërprerë, baza e të dhënave e ekzekuton atë dhe bën ndryshime në ditë. Ndodhur në gjendjen në momentin e dështimit, baza e të dhënave lexon ditën në rend të drejtpërdrejtë dhe rikthen ndryshimet e bëra nga transaksionet. Kështu ruhet qëndrueshmëria e transaksioneve që tashmë janë konfirmuar dhe thellësia e transaksionit të ndërprerë.
Thjesht ekzekutimi i përsëritur i transaksioneve të pasakta nuk është i mjaftueshëm për rikuperimin.
Shembull. Në llogarinë e përdoruesit ka 500$ dhe përdoruesi vendos të tërheqë ato përmes bankomati. Kryhen dy transaksione. E para lexon vlerën e balanës dhe nëse ka mjete të mjaftueshme në llogari, i jep paratë përdoruesit. E dyta heq nga bilanci shumën përkatëse. Supozoni se ndodhi një dështim në sistem dhe operacioni i parë nuk u realizua, ndërsa i dyti u realizua. Në këtë rast ne nuk mund të japim përsëri paratë përdoruesit pa rikthyer sistemin në gjendjen e tij fillestare me një bilanc pozitiv.
Nivelet e izolimit
Leximi i të dhënave të fiksuara (Read Committed)
Problemi i leximit të papastërt (Dirty Read) qëndron në faktin se transaksioni mund të lexojë një rezultat të përkohshëm të një transaksioni tjetër.
Shembull. Vlera fillestare e balansit është 0$. T1 shton 50$ në bilanc. T2 lexon vlerën e balansit (50$). T1 anullon ndryshimet dhe përfundon. T2 vazhdon ekzekutimin me të dhëna të gabuara për bilancin.
Zgjidhja është leximi i të dhënave të fiksuara (Read Committed) që ndalon leximin e të dhënave të modifikuara nga një transaksion. Nëse transaksioni A ndryshon një grup të dhënash, atëherë transaksioni B kur kërkon këto të dhëna, detyrohet të presë përfundimin e transaksionit A.
Leximi i përsëritur (Repeatable Read)
Problemi i humbjes së ndryshimeve (Lost Updates). T1 ruan ndryshimet mbi ndryshimet e T2.
Shembull. Vlera fillestare e balansit është 0$ dhe dy transaksione e mbushin bilancin njëkohësisht. T1 dhe T2 lexojnë bilancin që është 0$. Më pas T2 shton 200$ në 0$ dhe ruan rezultatin. T1 shton 100$ në 0$ dhe ruan rezultatin. Rezultati përfundimtar është 100$ në vend të 300$.
Problemi i leximit të pa përsëritur (Unrepeatable read). Leximi përsëritës i të dhënave të njëjta kthen vlera të ndryshme.
Shembull. T1 lexon vlerën e balansit që është 0$. Më pas T2 shton 50$ në bilanc dhe përfundon. T1 përsëri lexon të dhënat dhe zbulon një mosmarrëveshje me rezultatin e mëparshëm.
Leximi i përsëritur (Repeatable Read) garanton që leximi përsëritës do të kthejë të njëjtin rezultat. Të dhënat e lexuara nga një transaksion nuk mund të ndryshojnë në të tjerat deri në përfundimin e transaksionit. Nëse transaksioni A lexon një grup të dhënash, atëherë transaksioni B kur kërkon këto të dhëna, detyrohet të presë përfundimin e transaksionit A.
Leximi i renditur (Serializable)
Problemi i leximit fantom (Phantom Reads). Dy kërkesa që zgjedhin të dhëna sipas një kushti kthejnë vlera të ndryshme.
Shembull. T1 kërkon numrin e të gjithë përdoruesve me një bilanc që është më shumë se 0$ por më pak se 100$. T2 heq 1$ nga përdoruesi me një bilanc prej 101$. T1 ekzekuton përsëri kërkesën.
Leximi i renditur (Serializable). Transaksionet kryhen si plotësisht të renditura. Ndalohet të përditësohen dhe të shtohen të dhëna që bien nën kushtet e kërkesës. Nëse transaksioni A ka kërkuar të dhënat e tërë tabelës, atëherë tabela e tërë bllokohet për transaksionet e tjera deri në përfundimin e transaksionit A.
Planifikuesi (Scheduler)
Përcakton rendin në të cilin duhet të kryhen operacionet gjatë transaksioneve që zhvillohen paralelisht.
Siguron një nivel të caktuar izolimi. Nëse rezultati i ekzekutimit të operacioneve nuk varet nga rendi i tyre, atëherë këto operacione janë komutative (Permutable). Operacionet e leximit dhe operacionet mbi të dhëna të ndryshme janë komutative. Operacionet e leximit-shkrimit dhe shkrimit-shkrimit nuk janë komutative. Detyra e planifikuesit është të ndërthuret operacionet që kryhen nga transaksionet paralel, në mënyrë që rezultati të jetë ekuivalent me ekzekutimin sekondar të transaksioneve.
Mekanizmat e kontrollit të detyrave paralel (Concurrency Control)
Optimisti bazohet në zbulimin dhe zgjidhjen e konflikteve, pesimisti në parandalimin e shfaqjes së konflikteve.
Me qasjen optimiste, disa përdorues marrin kopje të të dhënave. Ai që përfundon i pari duke redaktuar ruan ndryshimet, ndërsa të tjerët duhet të kryejnë një bashkim të ndryshimeve. Algoritmi optimist lejon që konflikti të ndodhë, por sistemi duhet të rikuperohet pas konfliktit.
Me qasjen pesimiste, përdoruesi i parë që kap të dhënat pengon marrjen e të dhënave nga të tjerët. Nëse konflikte janë të rralla, është e arsyeshme të zgjidhet strategjia optimiste, pasi ajo siguron një nivel më të lartë paralelizmi.
Bllokimi (Locking)
Nëse një transaksion ka bllokuar të dhënat, atëherë transaksionet e tjera që kërkojnë të dhëna duhet të presin të zbllokohen.
Një bllokim mund të aplikohet mbi një bazë të dhënash, tryezë, rresht ose atribut. Bllokimi i ndarë (Shared Lock) mund të aplikohet mbi të dhëna të caktuara nga disa transaksione, duke lejuar leximin nga të gjitha transaksionet (përfshirë atë që e ka vendosur) dhe duke ndaluar ndryshimin dhe bllokimin ekskluziv. Bllokimi ekskluziv (Exclusive Lock) mund të aplikohet vetëm nga një transaksion, duke lejuar çdo veprim të transaksionit që e ka aplikuar, duke ndaluar veprimet e tjera.
Një bllokim i ndërsjellë konsiderohet një situatë kur transaksionet ndodhen në një gjendje pritjeje që zgjat pafundësisht.
Shembuj. Transaksioni i parë pret lirimin e të dhënave të bllokuara nga i dyti, ndërsa i dyti pret lirimin e të dhënave të bllokuara nga i pari.
Zgjidhja optimiste e problemit të bllokimeve të ndërsjella lejon që bllokimi të ndodhi, por më pas rikthen sistemin duke anuluar një nga transaksionet e involvuara në bllokim.
Me një periudhë të caktuar kohore, kërkohen bllokimet e ndërsjella. Një nga metodat e identifikimit është përmes kohës, që do të thotë se supozohet që ka ndodhur bllokimi nëse një transaksion është duke u ekzekutuar për një periudhë shumë të gjatë. Kur një bllokim i ndërsjellë është gjetur, një nga transaksionet anulohet, duke lejuar që transaksionet e tjera pjesëmarrëse të përfundojnë. Zgjedhja e viktimës mund të bazohet në koston e transaksioneve ose të tyre moshuar (skemat Wait-Die dhe Wound-wait).
Çdo transaksion T i jepet një markë kohore TS e cila përmban kohën e fillimit të ekzekutimit të transaksionit.
Wait-Die.
Nëse TS(Ti) < TS(Tj), atëherë Ti pret, përndryshe Ti anulohet dhe fillon përsëri me të njëjtën markë kohore.
Nëse një transaksion më i ri ka bllokuar një burim, ndërsa një më i vjetër kërkon të njëjtin burim, atëherë transaksionit më të vjetër i lejohet të presë. Nëse një transaksion më i vjetër ka bllokuar burimin, atëherë transaksioni më i ri që kërkon këtë burim do të anulohet.
Wound-wait.
Nëse TS(Ti) < TS(Tj), atëherë Tj anulohet dhe fillon përsëri me të njëjtën markë kohore, përndryshe Ti pret.
Nëse një transaksion më i ri ka zënë një burim dhe një transaksion më i vjetër kërkon gjithashtu këtë burim, atëherë transaksioni më i ri do të anulohet. Nëse një transaksion më i vjetër ka zënë burimin, atëherë transaksioni më i ri, që kërkon këtë burim, lejohet të presë. Zgjedhja e viktimës e bazuar në moshën parandalon ndodhin e bllokimeve, por anullon transaksionet që nuk janë në gjendje bllokimi. Problemi qëndron në faktin se transaksionet mund të anullohen shumë herë, pasi një transaksion më i vjetër mund të mbajë një burim për një kohë të gjatë.
Një zgjidhje pesimiste për problemin e bllokimeve nuk lejon që një transaksion të fillojë ekzekutimin nëse ka rrezik për ndodhin e bllokimit.
Për të zbuluar bllokimin, ndërtohet një grafik (grafiku i pritjes, wait-for-graph), ku kulmet janë transaksionet dhe arkat janë të orientuara nga transaksionet që presin lirimin e të dhënave drejt transaksioneve që kanë zënë këto të dhëna. Kunderthënia ndodh nëse grafiku ka ciklikë. Ndërtimi i grafikut të pritjes, sidomos në DB të shpërndara, është një procedurë e shtrenjtë.
Bllokimi dyfazor — parandalimi i bllokimeve përmes kapjes së të gjithë burimeve të përdorura nga transaksioni në fillim të transaksionit dhe lirimi i tyre në fund.
Të gjitha operacionet bllokuese duhet të parashikojnë të parin që do të lirohet. Ka dy faza — Faza e Rritjes gjatë së cilës ndërtohen kapjet dhe Faza e Tkurrjes gjatë së cilës ndodh lirimi i kapjeve. Në rast të pamundësisë për të kapur një nga burimet, transaksioni fillon nga e para. Mund të ndodhë që një transaksion të mos jetë në gjendje të merret me burimet e nevojshme, për shembull nëse disa transaksione garojnë për të njëjtat burime.
Komitimi dyfazor siguron ekzekutimin e komitit në të gjitha replikat e DB.
Çdo DB regjistron informacionin për të dhënat që do të ndryshohen në log dhe i përgjigjet koordinatorit me OK (Faza e Votimit). Pasi të gjithë të përgjigjen me OK, koordinatorët dërgojnë një sinjal që detyron të gjithë të bëjnë komitin. Pas komitit, server të gjithë përgjigjen me OK; nëse ndonjëra nuk përgjigjet me OK, koordinatorët dërgojnë një sinjal për të anuluar ndryshimet në të gjitha serverat (Faza e Përfundimit).
Metoda e vulave të kohës.
Një transaksion më i vjetër anullohet kur përpiqet të qaset në të dhënat, të përfshira nga një transaksion më i ri.
Çdo transaksioni i caktohet një timestamp TS që i përket kohës së fillimit të ekzekutimit. Nëse Ti më i vjetër Tj, atëherë TS(Ti) < TS(Tj).
Kur transaksioni rikthehet, i caktohet një timestamp i ri. Çdo objekt të dhënash Q i angazhuar nga transaksioni shënohet me dy timestamps. W-TS(Q) — timestamp-i i transaksionit më të ri, që ka realizuar një shënim mbi Q. R-TS(Q) — timestamp-i i transaksionit më të ri, që ka realizuar një shënim lexim mbi Q.
Kur transaksioni T kërkon të lexojë të dhëna Q dy skenarë janë të mundshëm.
Nëse TS(T) < W-TS(Q), pra nëse të dhënat janë përditësuar nga një transaksion më i ri, atëherë transaksioni T rikthehet.
Nëse TS(T) >= W-TS(Q), atëherë leximi kryhet dhe R-TS(Q) kthehet në MAX(R-TS(Q), TS(T)).
Kur transaksioni T kërkon të ndryshojë të dhënat Q dy skenarë janë të mundshëm.
Nëse TS(T) < R-TS(Q), pra që të dhënat tashmë janë lexuar nga një transaksion më i ri dhe nëse bëhet një ndryshim, do të ndodhë një konflikt. Transaksioni T rikthehet.
Nëse TS(T) < W-TS(Q), pra transaksioni përpiqet të shkruajë një vlerë më të re, transaksioni T rikthehet. Në raste të tjera, ndryshimi kryhet dhe W-TS(Q) bëhet i barabartë me TS(T).
Nuk kërkohet ndërtimi i shtrenjtë i një grafiku pritjeje. Transaksionet më të vjetra varen nga ato më të reja, kështu që në grafikun e pritjes nuk ka cikle. Nuk ka bllokime reciproke, pasi transaksionet nuk presin, por menjëherë rikthehen. Rikthime kaskadë janë të mundshme. Nëse Ti u rikthye, dhe Tj lexoi të dhëna që ndryshoi Ti, atëherë Tj duhet të rikthehet gjithashtu. Nëse gjatë kësaj Tj tashmë ishte angazhuar, atëherë do të ndodhte një shkelje e parimit të qëndrueshmërisë.
Një nga zgjidhjet për rikthime kaskadë. Transaksioni kryen të gjitha operacionet shënimi në fund, ku transaksionet e tjera janë të detyruara të presin për përfundimin e kësaj operacioni. Transaksionet presin angazhimin përpara leximit.
Rregulli i shkrimit të Thomas — një variacion i metodës së timestamps ku të dhënat e përditësuara nga një transaksion më i ri nuk lejohet të shkruhen nga një më të vjetër.
Transaksioni T kërkon të ndryshojë të dhënat Q. Nëse TS(T) < W-TS(Q), pra transaksioni përpiqet të shkruajë një vlerë më të re, transaksioni T nuk rikthehet si në metodën e timestamps.
Burimi: habr.com
