Transakcie v globále InterSystems IRIS

Transakcie v globále InterSystems IRISInterSystems IRIS DBMS podporuje zaujímavé štruktúry na ukladanie dát - globals. V podstate ide o viacúrovňové kľúče s rôznymi doplnkovými vychytávkami v podobe transakcií, rýchlych funkcií na prechádzanie dátových stromov, zámkov a vlastného jazyka ObjectScript.

Prečítajte si viac o globáloch v sérii článkov „Globálne sú meče pokladov na ukladanie údajov“:

Stromy. Časť 1
Stromy. Časť 2
Riedke polia. Časť 3

Začal som sa zaujímať o to, ako sa transakcie implementujú v globaloch, aké sú tam funkcie. Ide predsa o úplne inú štruktúru na ukladanie údajov ako bežné tabuľky. Oveľa nižšia úroveň.

Ako je známe z teórie relačných databáz, dobrá implementácia transakcií musí spĺňať požiadavky ACID:

A - Atómová (atómová). Zaznamenajú sa všetky zmeny vykonané v transakcii alebo žiadne zmeny.

C - Konzistentnosť. Po dokončení transakcie musí byť logický stav databázy vnútorne konzistentný. Táto požiadavka sa v mnohom týka programátora, no v prípade SQL databáz aj cudzích kľúčov.

I - Izolovať. Súbežne prebiehajúce transakcie by sa nemali navzájom ovplyvňovať.

D - Odolný. Po úspešnom dokončení transakcie by problémy na nižších úrovniach (napríklad výpadok napájania) nemali ovplyvniť údaje zmenené transakciou.

Globálne sú nerelačné dátové štruktúry. Boli navrhnuté tak, aby fungovali super rýchlo na veľmi obmedzenom hardvéri. Pozrime sa na implementáciu transakcií v globálnych pomocou oficiálny obrázok dokovacej stanice IRIS.

Na podporu transakcií v IRIS sa používajú nasledujúce príkazy: TSTART, TCOMMIT, TROLLBACK.

1. Atomicita

Najjednoduchší spôsob kontroly je atomicita. Kontrolujeme z konzoly databázy.

Kill ^a
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TCOMMIT

Potom dospejeme k záveru:

Write ^a(1), “ ”, ^a(2), “ ”, ^a(3)

Dostaneme:

1 2 3

Všetko je v poriadku. Atomicita je zachovaná: všetky zmeny sú zaznamenané.

Skomplikujme si úlohu, predstavme chybu a uvidíme, ako sa transakcia uloží, čiastočne alebo vôbec.

Ešte raz skontrolujeme atomicitu:

Kill ^A
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3

Potom nádobu nasilu zastavíme, spustíme a uvidíme.

docker kill my-iris

Tento príkaz je takmer ekvivalentný vynútenému vypnutiu, pretože vysiela signál SIGKILL na okamžité zastavenie procesu.

Možno sa transakcia čiastočne zachránila?

WRITE ^a(1), ^a(2), ^a(3)
^
<UNDEFINED> ^a(1)

- Nie, neprežilo.

Skúsme príkaz na vrátenie:

Kill ^A
TSTART
Set ^a(1) = 1
Set ^a(2) = 2
Set ^a(3) = 3
TROLLBACK

WRITE ^a(1), ^a(2), ^a(3)
^
<UNDEFINED> ^a(1)

Nič sa tiež nezachovalo.

2. Dôslednosť

Keďže v databázach založených na globáloch sa kľúče vyrábajú aj na globáloch (pripomínam, že globálna je štruktúra nižšej úrovne na ukladanie údajov ako relačná tabuľka), na splnenie požiadavky konzistencie je potrebné zahrnúť zmenu kľúča v rovnakej transakcii ako zmena v globálnom.

Napríklad máme globálneho ^person, v ktorom ukladáme osobnosti a ako kľúč používame DIČ.

^person(1234567, ‘firstname’) = ‘Sergey’
^person(1234567, ‘lastname’) = ‘Kamenev’
^person(1234567, ‘phone’) = ‘+74995555555
...

Aby sme mali rýchle vyhľadávanie podľa priezviska a mena, vytvorili sme kľúč ^index.

^index(‘Kamenev’, ‘Sergey’, 1234567) = 1

Aby bola databáza konzistentná, musíme osobu pridať takto:

TSTART
^person(1234567, ‘firstname’) = ‘Sergey’
^person(1234567, ‘lastname’) = ‘Kamenev’
^person(1234567, ‘phone’) = ‘+74995555555
^index(‘Kamenev’, ‘Sergey’, 1234567) = 1
TCOMMIT

Preto pri odstraňovaní musíme použiť aj transakciu:

TSTART
Kill ^person(1234567)
ZKill ^index(‘Kamenev’, ‘Sergey’, 1234567)
TCOMMIT

Inými slovami, splnenie požiadavky konzistentnosti spočíva výlučne na pleciach programátora. Ale pokiaľ ide o globálne, je to normálne, vzhľadom na ich nízkoúrovňovú povahu.

3. Izolácia

Tu začína divočina. Mnoho používateľov súčasne pracuje na rovnakej databáze a mení rovnaké údaje.

Situácia je porovnateľná s prípadom, keď veľa používateľov súčasne pracuje s rovnakým úložiskom kódu a pokúšajú sa súčasne odovzdať zmeny do mnohých súborov naraz.

Databáza by to všetko mala vyriešiť v reálnom čase. Vzhľadom na to, že v serióznych firmách je dokonca aj špeciálna osoba, ktorá je zodpovedná za kontrolu verzií (za zlučovanie pobočiek, riešenie konfliktov a pod.), a to všetko musí databáza robiť v reálnom čase, zložitosť úlohy a správnosť návrh databázy a kód, ktorý jej slúži.

Databáza nedokáže pochopiť význam akcií vykonaných používateľmi, aby sa predišlo konfliktom, ak pracujú s rovnakými údajmi. Môže vrátiť späť iba jednu transakciu, ktorá je v konflikte s inou, alebo ich vykonať postupne.

Ďalším problémom je, že počas vykonávania transakcie (pred potvrdením) môže byť stav databázy nekonzistentný, preto je žiaduce, aby ostatné transakcie nemali prístup k nekonzistentnému stavu databázy, ktorý sa dosahuje v relačných databázach. mnohými spôsobmi: vytváranie snímok, riadky s viacerými verziami atď.

Pri paralelnom vykonávaní transakcií je pre nás dôležité, aby sa navzájom nerušili. Toto je vlastnosť izolácie.

SQL definuje 4 úrovne izolácie:

  • PREČÍTAJTE SI BEZ DOPORUČENIA
  • PREČÍTAJTE SI ZÁVÄZOK
  • OPAKOVANÉ PREČÍTANIE
  • SERIALIZOVATEĽNÝ

Pozrime sa na každú úroveň samostatne. Náklady na implementáciu každej úrovne rastú takmer exponenciálne.

PREČÍTAJTE SI BEZ DOPORUČENIA - toto je najnižšia úroveň izolácie, ale zároveň najrýchlejšia. Transakcie môžu čítať zmeny, ktoré vykonali navzájom.

PREČÍTAJTE SI ZÁVÄZOK je ďalšou úrovňou izolácie, ktorá je kompromisom. Transakcie si nemôžu navzájom prečítať zmeny pred odovzdaním, ale môžu prečítať všetky zmeny vykonané po odovzdaní.

Ak máme dlhú transakciu T1, počas ktorej prebiehali commity v transakciách T2, T3 ... Tn, ktoré pracovali s rovnakými dátami ako T1, tak pri vyžiadaní dát v T1 dostaneme zakaždým iný výsledok. Tento jav sa nazýva neopakovateľné čítanie.

OPAKOVANÉ PREČÍTANIE — na tejto úrovni izolácie nemáme fenomén neopakovateľného čítania, pretože pri každej požiadavke na čítanie dát sa vytvorí snímka výsledkových dát a pri opätovnom použití v tej istej transakcii dáta zo snímky sa používa. Na tejto úrovni izolácie je však možné čítať fantómové dáta. Týka sa to čítania nových riadkov, ktoré boli pridané paralelnými potvrdenými transakciami.

SERIALIZOVATEĽNÝ — najvyššia úroveň izolácie. Vyznačuje sa tým, že dáta použité akýmkoľvek spôsobom v transakcii (čítanie alebo zmena) sa sprístupnia ďalším transakciám až po dokončení prvej transakcie.

Najprv zistime, či existuje izolácia operácií v transakcii od hlavného vlákna. Otvorme 2 terminálové okná.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

Neexistuje žiadna izolácia. Jedno vlákno vidí, čo robí druhý, ktorý otvoril transakciu.

Pozrime sa, či transakcie rôznych vlákien vidia, čo sa v nich deje.

Otvorme 2 terminálové okná a paralelne otvorme 2 transakcie.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Paralelné transakcie vidia navzájom svoje údaje. Dostali sme teda najjednoduchšiu, ale aj najrýchlejšiu úroveň izolácie, ČÍTAJTE BEZZÁVÄZNE.

V zásade by sa to dalo očakávať u globalistov, pre ktorých bol výkon vždy prioritou.

Čo ak potrebujeme vyššiu úroveň izolácie v operáciách na globálnej úrovni?

Tu sa musíte zamyslieť nad tým, prečo sú úrovne izolácie vôbec potrebné a ako fungujú.

Najvyššia úroveň izolácie SERIALIZE znamená, že výsledok paralelne vykonávaných transakcií je ekvivalentný ich postupnému vykonávaniu, čo zaručuje absenciu kolízií.

Môžeme to urobiť pomocou inteligentných zámkov v ObjectScript, ktoré majú mnoho rôznych použití: môžete vykonávať pravidelné, prírastkové, viacnásobné zamykanie pomocou príkazu LOCK.

Nižšie úrovne izolácie sú kompromisy navrhnuté na zvýšenie rýchlosti databázy.

Pozrime sa, ako môžeme pomocou zámkov dosiahnuť rôzne úrovne izolácie.

Tento operátor vám umožňuje prevziať nielen exkluzívne zámky potrebné na zmenu údajov, ale aj takzvané zdieľané zámky, ktoré môžu zaberať niekoľko vlákien paralelne, keď potrebujú čítať údaje, ktoré by nemali byť zmenené inými procesmi počas procesu čítania.

Viac informácií o metóde dvojfázového blokovania v ruštine a angličtine:

Dvojfázové blokovanie
Dvojfázové uzamykanie

Problém je v tom, že počas transakcie môže byť stav databázy nekonzistentný, ale tieto nekonzistentné údaje sú viditeľné pre iné procesy. Ako sa tomu vyhnúť?

Pomocou zámkov vytvoríme okná viditeľnosti, v ktorých bude stav databázy konzistentný. A všetok prístup do takýchto okien viditeľnosti dohodnutého stavu bude kontrolovaný zámkami.

Zdieľané zámky na rovnakých údajoch sú opätovne použiteľné – môže ich zabrať niekoľko procesov. Tieto zámky bránia iným procesom meniť údaje, t.j. používajú sa na vytvorenie okien konzistentného stavu databázy.

Na zmeny údajov sa používajú exkluzívne zámky – takýto zámok môže prijať iba jeden proces. Exkluzívny zámok môže získať:

  1. Akýkoľvek proces, ak sú údaje voľné
  2. Iba proces, ktorý má zdieľaný zámok na tieto údaje a ako prvý požiadal o exkluzívny zámok.

Transakcie v globále InterSystems IRIS

Čím užšie je okno viditeľnosti, tým dlhšie naň musia ostatné procesy čakať, ale o to konzistentnejší môže byť stav databázy v ňom.

READ_COMMITTED — podstatou tejto úrovne je, že vidíme iba potvrdené údaje z iných vlákien. Ak údaje v inej transakcii ešte neboli potvrdené, vidíme ich starú verziu.

To nám umožňuje paralelizovať prácu namiesto čakania na uvoľnenie zámku.

Bez špeciálnych trikov neuvidíme starú verziu údajov v IRIS, takže si budeme musieť vystačiť so zámkami.

V súlade s tým budeme musieť použiť zdieľané zámky, aby sme umožnili čítanie údajov iba v momentoch konzistentnosti.

Povedzme, že máme užívateľskú základňu ^osobu, ktorá si navzájom prevádza peniaze.

Moment prevodu z osoby 123 na osobu 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)

Okamžik vyžiadania sumy peňazí od osoby 123 pred zaúčtovaním musí byť sprevádzaný exkluzívnym blokovaním (štandardne):

LOCK +^person(123)
Write ^person(123)

A ak potrebujete zobraziť stav účtu vo svojom osobnom účte, môžete použiť zdieľaný zámok alebo ho nepoužívať vôbec:

LOCK +^person(123)#”S”
Write ^person(123)

Ak však predpokladáme, že databázové operácie sa vykonávajú takmer okamžite (dovoľte mi pripomenúť, že globály sú štruktúrou oveľa nižšej úrovne ako relačná tabuľka), potreba tejto úrovne klesá.

OPAKOVANÉ PREČÍTANIE - Táto úroveň izolácie umožňuje viacnásobné čítanie údajov, ktoré môžu byť modifikované súbežnými transakciami.

V súlade s tým budeme musieť použiť zdieľaný zámok na čítanie údajov, ktoré meníme, a výhradné zámky na údaje, ktoré meníme.

Našťastie operátor LOCK umožňuje v jednom výpise podrobne vypísať všetky potrebné zámky, ktorých môže byť veľa.

LOCK +^person(123, amount)#”S”
чтение ^person(123, amount)

iné operácie (v súčasnosti sa paralelné vlákna pokúšajú zmeniť ^person(123, množstvo), ale nemôžu)

LOCK +^person(123, amount)
изменение ^person(123, amount)
LOCK -^person(123, amount)

чтение ^person(123, amount)
LOCK -^person(123, amount)#”S”

Keď uvádzate zámky oddelené čiarkami, berú sa postupne, ale ak to urobíte:

LOCK +(^person(123),^person(242))

potom sa berú atómovo všetky naraz.

serializáciu — budeme musieť nastaviť zámky, aby sa nakoniec všetky transakcie, ktoré majú spoločné dáta, vykonávali postupne. Pre tento prístup by väčšina zámkov mala byť exkluzívna a mala by sa použiť na najmenších oblastiach globálneho výkonu.

Ak hovoríme o debetovaní prostriedkov v globálnej ^osobe, potom je pre ňu prijateľná iba úroveň izolácie SERIALIZE, pretože peniaze sa musia minúť striktne postupne, inak je možné minúť rovnakú sumu niekoľkokrát.

4. Trvanlivosť

Uskutočnil som testy s tvrdým rezaním nádoby pomocou

docker kill my-iris

Baza ich znášala dobre. Neboli zistené žiadne problémy.

Záver

Pre globálnych používateľov má InterSystems IRIS podporu transakcií. Sú skutočne atómové a spoľahlivé. Na zabezpečenie konzistencie databázy založenej na globáloch je potrebné úsilie programátora a použitie transakcií, pretože nemá zložité vstavané konštrukcie, ako sú cudzie kľúče.

Úroveň izolácie globálov bez použitia zámkov je READ UNCOMMITED a pri použití zámkov môže byť zabezpečená až do úrovne SERIALIZÁCIE.

Správnosť a rýchlosť transakcií na globaloch veľmi závisí od zručnosti programátora: čím viac zdieľaných zámkov sa používa pri čítaní, tým vyššia je úroveň izolácie a čím užšie sa berú exkluzívne zámky, tým je výkon rýchlejší.

Zdroj: hab.com

Pridať komentár