Baza e tĂ« dhĂ«nave InterSystems IRIS mbĂ«shtet struktura interesante pĂ«r ruajtjen e tĂ« dhĂ«nave â globalet. NĂ« thelb, kĂ«to janĂ« çelĂ«sa me shumĂ« nivele me disa avantazhe shtesĂ« si transaksionet, funksionet e shpejta pĂ«r navigimin e pemĂ«ve tĂ« tĂ« dhĂ«nave, bllokimet dhe gjuhĂ«n e saj ObjectScript.
MĂ« shumĂ« pĂ«r globalet nĂ« ciklin e artikujve "Globalet â shpata-magazina pĂ«r ruajtjen e tĂ« dhĂ«nave":
Më interesonte se si janë realizuar transaksionet në globalet, çfarë veçorish ka aty. Kjo është një strukturë krejtësisht e ndryshme për ruajtjen e të dhënave se sa tabelat që na janë bërë të zakonshme. Ndërsa është shumë më e ulët në nivel.
Siç dihet nga teoria e bazave të dhënave relacional, një implementim i mirë i transaksioneve duhet të plotësojë kërkesat :
A â Atomike (atomariteti). TĂ« gjitha ndryshimet e bĂ«ra nĂ« transaksion regjistrohen ose nuk regjistrohet asnjĂ«.
C â KonsistencĂ« (kontradiktor). Pas pĂ«rfundimit tĂ« transaksionit, gjendja logjike e bazĂ«s sĂ« tĂ« dhĂ«nave duhet tĂ« jetĂ« brenda vetĂ«-konsistente. NĂ« njĂ« farĂ« mĂ«nyre, kjo kĂ«rkesĂ« i pĂ«rket programuesit, por nĂ« rastin e bazave tĂ« dhĂ«nave SQL, ajo i pĂ«rket gjithashtu çelĂ«save tĂ« jashtĂ«m.
I â Izolimi (izolueshmĂ«ria). Transaksionet qĂ« ekzekutohen paralelisht nuk duhet tĂ« ndikojnĂ« nĂ« njĂ«ra-tjetrĂ«n.
D â QĂ«ndrueshmĂ«ri (qĂ«ndrueshmĂ«ria). Pas pĂ«rfundimit tĂ« suksesshĂ«m tĂ« transaksionit, problemet nĂ« nivelet e poshtme (siç Ă«shtĂ« dĂ«shtimi i furnizimit me energji) nuk duhet tĂ« ndikojnĂ« nĂ« tĂ« dhĂ«nat e ndryshuara nga transaksioni.
Globale janë struktura të dhënash jo-reale. Ato janë krijuar për të punuar me shpejtësi shumë të lartë në hardware shumë të kufizuar. Le të shqyrtojmë implementimin e transaksioneve në globale duke përdorur .
Për të mbështetur transaksionet në IRIS përdoren komandat: , , .
1. Atomikësia
Më e lehta për të verifikuar është atomikësia. Kontroloni nga konsola e bazës së të dhënave.
Kill ^a
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TCOMMITMë pas, nxjerrim përfundimin:
Write ^a(1), â â, ^a(2), â â, ^a(3)Do tĂ« marrim:
1 2 3E gjitha është në rregull. Atomikësia është ruajtur: të gjitha ndryshimet u regjistruan.
Të komplikojmë punën, të introduktojmë një gabim dhe të shohim si do të ruhet transaksioni, pjesërisht ose aspak.
Le ta verifikojmë përsëri atomikësinë:
Kill ^A
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3Pas kësaj, do të ndalojmë me forcë kontejnerin, do ta rikthejmë dhe do të shohim.
docker kill my-irisKy kjo komandë është praktikisht ekuivalente me një ndërprerje të dhunshme të energjisë, pasi dërgon sinjalin e menjëhershëm të ndalimit të procesit SIGKILL.
A mund të jetë transaksioni ruajtur pjesërisht?
WRITE ^a(1), ^a(2), ^a(3)
^
^a(1)â Jo, nuk Ă«shtĂ« ruajtur.
Le të testojmë komandën e rikthimit:
Kill ^A
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TROLLBACK
WRITE ^a(1), ^a(2), ^a(3)
^
^a(1)Asgjë nuk u ruajt.
2. Konsistenca
Pasi për çelësat në globalë, çelësat krijohen gjithashtu në globalë (të kujtojmë se globali është një strukturë më e ulët për ruajtjen e të dhënave se sa tabela relacional), për të përmbushur kërkesën e konsistencës, është e nevojshme që ndryshimi i çelësit të përfshihet në të njëjtën transaksion si ndryshimi i globalit.
Për shembull, ne kemi një global ^person, ku ruajmë personat dhe si çelës përdorim INN.
^person(1234567, âfirstnameâ) = âSergeyâ
^person(1234567, âlastnameâ) = âKamenevâ
^person(1234567, âphoneâ) = â+74995555555
...Për të pasur një kërkim të shpejtë sipas mbiemrit dhe emrit, ne kemi krijuar çelësin ^index.
^index(âKamenevâ, âSergeyâ, 1234567) = 1PĂ«r tĂ« ruajtur konsistencĂ«n e bazĂ«s, ne duhet ta shtojmĂ« personin kĂ«shtu:
TSTART
^person(1234567, âfirstnameâ) = âSergeyâ
^person(1234567, âlastnameâ) = âKamenevâ
^person(1234567, âphoneâ) = â+74995555555
^index(âKamenevâ, âSergeyâ, 1234567) = 1
TCOMMITPërkatësisht, kur fshihen, ne gjithashtu duhet të përdorim një transaksion:
TSTART
Kill ^person(1234567)
ZKill ^index(âKamenevâ, âSergeyâ, 1234567)
TCOMMITNĂ« fjalĂ« tĂ« tjera, pĂ«rmbushja e kĂ«rkesĂ«s pĂ«r qĂ«ndrueshmĂ«ri Ă«shtĂ« plotĂ«sisht nĂ« duar tĂ« programuesit. Por kur flasim pĂ«r globale â kjo Ă«shtĂ« e pranueshme, pĂ«r shkak tĂ« natyrĂ«s sĂ« tyre tĂ« nivelit tĂ« ulĂ«t.
3. Izolimi
Këtu fillojnë kompleksitetet. Shumë përdorues punojnë njëkohësisht me të njëjtën bazë, duke modifikuar të njëjtat të dhëna.
Situata është krahasueshme me atë kur shumë përdorues punojnë njëkohësisht me të njëjtin depo me kod dhe përpiqen që njëkohësisht të kryejnë ndryshime në shumë skedarë.
Baza e të dhënave duhet ta menaxhojë gjithçka në kohë reale. Duke pasur parasysh se në kompanitë e mëdha ka madje edhe një person të veçantë që merret me kontrollin e versioneve (për bashkimet e degëve, zgjidhjen e konflikteve, etj.), dhe që Baza e të Dhënave duhet ta bëjë gjithçka në kohë reale, bëhet e qartë sa e ndërlikuar është kjo detyrë dhe saktësia në projektimin e bazës së të dhënave dhe kodit që e mbështet atë.
Baza e të dhënave nuk mund të kuptojë kuptimin e veprimeve që kryejnë përdoruesit për të parandaluar konfliktet nëse ata punojnë mbi të njëjtat të dhëna. Ajo mund ta anulojë vetëm një transaksion që është në kontradiktë me një tjetër ose t'i kryejë ato njëri pas tjetrit.
Një problem tjetër është se gjatë ekzekutimit të transaksionit (para komitimit), gjendja e bazës mund të jetë e paqëndrueshme, prandaj është e preferueshme që transaksioneve të tjera t'u mohohet akses në gjendjen e paqëndrueshme të bazës së të dhënave, gjë që arrihet në bazat e të dhënave relationale në shumë mënyra: duke krijuar snapshot-e, shumëversionalizmin e rreshtave, etj.
Kur ekzekutohen paralelisht transaksionet, është e rëndësishme që ato të mos ndërhyjnë njëra me tjetrën. Kjo është veti izoluese.
SQL përcakton 4 nivele izolimi:
- LEXO PA DĂSHTIM
- LEXO DĂSHTIM TĂ PĂRMBAJTUR
- LEXO PĂRMBAJTESHĂM
- SERIALIZABLE
Le të shqyrtojmë çdo nivel në veçanti. Shpenzimet për zbatimin e secilit nivel rriten gati në mënyrë eksponenciale.
LEXO PA DĂSHTIM â ky Ă«shtĂ« niveli mĂ« i ulĂ«t i izolimit, por gjithashtu mĂ« i shpejtĂ«. Transaksionet mund tĂ« lexojnĂ« ndryshimet qĂ« bĂ«jnĂ« njĂ«ra-tjetrĂ«n.
LEXO DĂSHTIM TĂ PĂRMBAJTUR â ky Ă«shtĂ« niveli i ardhshĂ«m i izolimit qĂ« Ă«shtĂ« njĂ« kompromis. Transaksionet nuk mund tĂ« lexojnĂ« ndryshimet qĂ« bĂ«jnĂ« njĂ«ra-tjetrĂ«n deri nĂ« angazhimin, por mund tĂ« lexojnĂ« çdo ndryshim qĂ« bĂ«het pas angazhimit.
Nëse kemi një transaksion të gjatë T1, gjatë të cilit ndodhin angazhime në transaksionet T2, T3 ⊠Tn, të cilat punojnë me të njëjtat të dhëna si T1, atëherë kur kërkojmë të dhëna në T1, ne do të marrim gjithmonë rezultate të ndryshme. Ky fenomen quhet lexim i përsëritur.
LEXO PĂRMBAJTESHĂM â nĂ« kĂ«tĂ« nivel izolimi nuk kemi fenomene tĂ« leximit tĂ« pĂ«rsĂ«ritur, pĂ«r shkak se pĂ«r çdo kĂ«rkesĂ« pĂ«r lexim tĂ« tĂ« dhĂ«nave krijohet njĂ« snapshot i rezultateve tĂ« tĂ« dhĂ«nave dhe, kur pĂ«rdoren pĂ«rsĂ«ri nĂ« kĂ«tĂ« transaksion, pĂ«rdoren tĂ« dhĂ«nat nga snapshot. MegjithatĂ«, nĂ« kĂ«tĂ« nivel izolimi, mund tĂ« ketĂ« lexime tĂ« tĂ« dhĂ«nave fantazmĂ«. KĂ«tu kuptojmĂ« leximin e rreshtave tĂ« rinj, tĂ« cilĂ«t janĂ« shtuar nga transaksione tĂ« tjera tĂ« fiksohme.
SERIALIZABLE â niveli mĂ« i lartĂ« i izolimit. Ai karakterizohet nga fakti se tĂ« dhĂ«nat qĂ« pĂ«rdoren nĂ« ndonjĂ« mĂ«nyrĂ« nĂ« transaksion (leximi ose ndryshimi) bĂ«hen tĂ« disponueshme pĂ«r transaksione tĂ« tjera vetĂ«m pasi tĂ« pĂ«rfundojĂ« transaksioni i parĂ«.
Së pari, le të shohim nëse ka izolim të operacioneve në transaksion nga rrjedha kryesore. Do të hapim 2 dritare terminali.
Kill ^t
Write ^t(1)
2
TSTART
Set ^t(1)=2Nuk ka izolim. Një rrjedhë sheh se çfarë bën tjetra që ka hapur transaksionin.
Le të shohim nëse transaksionet e rrjedhave të ndryshme shohin atë që ndodh brenda tyre.
Do të hapim 2 dritare terminali dhe do të hapim 2 transaksione paralelisht.
kill ^t
TSTART
Write ^t(1)
3
TSTART
Set ^t(1)=3
Transaksionet paralele shohin të dhënat njëra-tjetrës. Pra, ne kemi arritur nivelin më të thjeshtë, por gjithashtu më të shpejtë të izolimit READ UNCOMMITTED.
Në princip, kjo mund të pritej për globalët, për të cilët performanca gjithmonë ka qenë prioritare.
ĂfarĂ« mund tĂ« bĂ«jmĂ« nĂ«se na nevojitet njĂ« nivel mĂ« i lartĂ« izolimi nĂ« operacionet e globalĂ«ve?
Këtu duhet të mendojmë se çfarë janë nivellet e izolimit dhe si funksionojnë ato.
Niveli më i lartë i izolimit SERIALIZE do të thotë që rezultati i transaksioneve që ekzekutohen paralelisht është ekuivalent me ekzekutimin e tyre në mënyrë sekuenciale, që garanton moskollision.
Kjo mund të arrihet me bllokime të mira në ObjectScript, të cilat kanë një mori mënyrash të ndryshme aplikimi: mund të bëjmë bllokim të zakonshëm, inkremental, apo shumëfish me komandën .
Niveli më i ulët i izolimit janë kompromise të destinuara për të rritur shpejtësinë e funksionimit të bazës së të dhënave.
Le të shohim si mund të arrijmë nivele të ndryshme izolimi përmes bllokimeve.
Ky operator lejon që të merrni jo vetëm bllokime ekskluzive, të nevojshme për modifikimin e të dhënave, por edhe bllokime të ashtuquajtura shared, të cilat mund të merren paralelisht nga disa rrjedha në të njëjtën kohë kur u nevojitet të lexojnë të dhëna që nuk duhet të modifikohen nga procese të tjera gjatë leximit.
Më shumë rreth metodës dy-fazore të bllokimeve në rusisht dhe anglisht:
â
â
Sfidimi është se gjatë transaksionit gjendja e bazës mund të jetë e pasigurt, megjithatë këto të dhëna të pasigurta janë të dukshme për proceset e tjera. Si ta shmangim këtë?
Ne do të krijojmë me ndihmën e bllokimeve këso dritare shikimi, në të cilat gjendja e bazës do ët jetë e harmonizuar. Dhe të gjitha kërkesat për këto dritare shikimi të gjendjes të harmonizuar do të kontrollohen nga bllokimet.
Bllokimet shared tĂ« tĂ« njĂ«jtave tĂ« dhĂ«na janĂ« shumĂ«fishueshme â ato mund tĂ« merren nga disa procese. KĂ«to bllokime ndalojnĂ« proceset e tjera nga modifikimi i tĂ« dhĂ«nave, domethĂ«nĂ« ato pĂ«rdoren pĂ«r formimin e dritareve tĂ« gjendjes tĂ« harmonizuar tĂ« DB.
Bllokadat ekskluzive pĂ«rdoren pĂ«r ndryshimet nĂ« tĂ« dhĂ«na â kĂ«tĂ« bllokim mund ta marrĂ« vetĂ«m njĂ« proces. Bllokimin ekskluziv mund ta marrĂ«:
- Cdo proces, nëse të dhënat janë të lira
- Vetëm ai proces që ka një bllokim shared për këto të dhëna dhe kërkoi i pari bllokimin ekskluziv.

Sa më e ngushtë të jetë dritarja e shikimit, aq më gjatë e presin proceset e tjera, por aq më të qëndrueshme mund të jetë gjendja e DB-së në të.
READ_COMMITED â thelbi i kĂ«tij niveli Ă«shtĂ« qĂ« ne shohim vetĂ«m tĂ« dhĂ«nat e konfirmuara nga rrjedhat e tjera. NĂ«se tĂ« dhĂ«nat nĂ« njĂ« transaksion tjetĂ«r ende nuk janĂ« konfirmuar, atĂ«herĂ« ne i shohim versionin e tyre tĂ« vjetĂ«r.
Kjo na lejon të përparojmë punën në vend të pritjes për lirimin e bllokimit.
Pa truke të veçanta, ne nuk do të jemi në gjendje të shohim versionin e vjetër të të dhënave në IRIS, kështu që do të na duhet të përballojmë me bllokimet.
Për pasojë, na duhet me ndihmën e bllokimeve shared të lejojmë leximin e të dhënave vetëm në momentet e qëndrueshmërisë.
Supozoni se kemi një bazë përdoruesish ^person, të cilët transferojnë para njëri-tjetrit.
Momenti i transfertës nga persona 123 tek persona 242:
LOCK +^person(123), +^person(242)
Set ^person(123, amount) = ^person(123, amount) - amount
Set ^person(242, amount) = ^person(242, amount) + amount
LOCK -^person(123), -^person(242)Momentin e kërkesës për sasinë e parave nga persona 123 para se të kryhet debiti duhet të shoqërohet nga një bllokim ekskluziv (me të drejtë):
LOCK +^person(123)
Write ^person(123)Nëse është e nevojshme të shfaqet gjendja e llogarisë në panelin personal, mund të përdoret bllokimi i ndarë ose të mos përdoret fare:
LOCK +^person(123)#âSâ
Write ^person(123)Megjithatë, nëse supozojmë se operacionet që punojnë me DB kryhen pothuajse menjëherë (të kujtojmë se globat janë një strukturë shumë më e ulët se një tabelë relacionale), nevoja për këtë nivel bie.
LEXO PĂRMBAJTESHĂM â nĂ« kĂ«tĂ« nivel izolimi lejohet qĂ« tĂ« ketĂ« disa lexime tĂ« tĂ« dhĂ«nave qĂ« mund tĂ« ndryshohen nga transaksione paralele.
Për rrjedhojë, do të na duhet të vendosim bllokim të ndarë për të lexuar të dhënat që ne i ndryshojmë dhe bllokime ekskluzive për të dhënat që ne i ndryshojmë.
Faleminderit, operatori LOCK lejon në një operator të detajuar të enumerosh të gjitha bllokimet e nevojshme, që mund të jenë shumë.
LOCK +^person(123, amount)#âSâ
Lexo ^person(123, amount)operacione të tjera (në këtë kohë, rrjedhat paralel po përpiqen të ndryshojnë ^person(123, amount), por nuk mundin)
LOCK +^person(123, amount)
ndryshimi i ^person(123, amount)
LOCK -^person(123, amount)
leximi i ^person(123, amount)
LOCK -^person(123, amount)#âSâKur renditen bllokimet me presje, ato merren njĂ«ra pas tjetrĂ«s, dhe nĂ«se bĂ«het kĂ«shtu:
LOCK +(^person(123),^person(242))atëherë ato merren atomikisht të gjitha së bashku.
SERIALIZE â na duhet tĂ« vendosim bllokimet nĂ« mĂ«nyrĂ« qĂ«, nĂ« fund, tĂ« gjitha transaksionet qĂ« kanĂ« tĂ« dhĂ«na tĂ« pĂ«rbashkĂ«ta tĂ« kryhen njĂ«ra pas tjetrĂ«s. PĂ«r kĂ«tĂ« qasje, shumica e bllokimeve duhet tĂ« jenĂ« ekskluzive dhe tĂ« merren nĂ« zonat mĂ« tĂ« vogla tĂ« globalit pĂ«r performancĂ«.
Nëse flasim për tërheqjet e fondeve në globalin ^person, atëherë për të pranohet vetëm niveli i izolimit SERIALIZE, pasi paratë duhet të shpenzohen me radhë, përndryshe është e mundur të shpenzohet e njëjta shumë disa herë.
4. Qëndrueshmëria
Kam bërë teste me shkyçjen ashpër të konteinerit përmes
docker kill my-irisBaza e tyre e përballoi mirë. Nuk u identifikuan probleme.
Përfundimi
Për globalët në InterSystems IRIS, mbështetja për transaksionet është e disponueshme. Ato janë vërtet atomike, të sigurta. Për të garantuar konsistencën e DB në globalë, nevojiten përpjekjet e programuesit dhe përdorimi i transaksioneve, pasi në të nuk ka struktura të ndërlikuara të ndërtuara, siç janë çelësat e jashtëm.
Niveli i izolimit te globalët pa përdorimin e bllokimeve është READ UNCOMMITED, dhe me përdorimin e bllokimeve, mund të arrihet deri në nivelin SERIALIZE.
Korrigjueshmëria dhe shpejtësia e transaksioneve te globalët varen shumë nga mjeshtëria e programuesit: sa më gjerësisht të përdoren bllokimet e përbashkëta gjatë leximit, aq më i lartë është niveli i izolimit, dhe sa më ngushtësisht të merren bllokimet ekskluzive, aq më shumë rritet performanca.
Burimi: habr.com
