Transakcie v globáloch InterSystems IRIS

Transakcie v globáloch InterSystems IRISDBMS InterSystems IRIS podporuje zaujímavé štruktúry na uchovávanie údajov — globály. V podstate sú to viacúrovňové kľúče s rôznymi ďalšími funkciami ako sú transakcie, rýchle funkcie na prechádzanie dátovými stromami, zámky a vlastný jazyk ObjectScript.

Podrobnosti o globáloch v sérii článkov „Globály — meče-klenoty na uchovávanie údajov“:

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

Zaujímalo ma, ako sú implementované transakcie v globáloch, aké sú tam zvláštnosti. Je to predsa úplne iná štruktúra na uchovávanie dát než všetkým známe tabuľky. Omnoho nízkoúrovňovejšia.

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

A — Atomic (atomickosť). Zaznamenávajú sa všetky zmeny vykonané v transakcii alebo sa neudržia žiadne.

S — Consistency (konzistentnosť). Po dokončení transakcie by malo byť logické stav DB interne konzistentné. Táto požiadavka sa do veľkej miery týka programátora, ale v prípade SQL databáz sa vzťahuje aj na vonkajšie kľúče.

I — Isolate (izolovanosť). Paralelne vykonávané transakcie by nemali ovplyvňovať jedna druhú.

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

Globály sú nerelačné štruktúry údajov. Boli vytvorené pre extrémne rýchlu prácu na veľmi obmedzenom hardvéri. Poďme preskúmať realizáciu transakcií v globáloch pomocou oficiálneho docker obrazu IRIS.

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

1. Atomickosť

Najjednoduchšie je overiť atomickosť. Skontrolujeme z konzoly databázy.

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

Potom urobíme výstup:

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

Dostaneme:

1 2 3

Všetko je v poriadku. Atomickosť bola dodržaná: všetky zmeny sa uložili.

Zložme úlohu, zaveďme chybu a pozrime sa, ako sa transakcia uloží, či čiastočne alebo vôbec nie.

Ešte raz skontrolujeme atomickosť:

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

Po čom násilne zastavíme kontajner, spustíme ho a pozrieme sa.

docker kill my-iris

Tento príkaz je prakticky ekvivalentný nútenému vypnutiu napájania, pretože posiela signál na okamžité zastavenie procesu SIGKILL.

Môže byť transakcia uložená čiastočne?

WRITE ^a(1), ^a(2), ^a(3)
^
 ^a(1)

— Nie, neuložila sa.

Vyskúšame 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)
^
 ^a(1)

Tiež sa nič neuložilo.

2. Konzistentnosť

Pretože v globalizovaných databázach sú kľúče vytvorené tiež na globálnych entitách (pripomínam, že globalizácia je nižšia štruktúra pre ukladanie dát než relačná tabuľa), na splnenie požiadavky konzistentnosti je potrebné zahrnúť zmenu kľúča do tej istej transakcie, ako aj zmenu globálu.

Napríklad máme global ^person, kde uchovávame osobní a ako kľúč používame IČO.

^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 pridávať osobu takto:

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

Odpovedajúco, pri odstraňovaní musíme tiež použiť transakciu:

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

Inými slovami, splnenie požiadavky konzistentnosti leží úplne na pleciach programátora. Ale keď ide o globály — je to v poriadku, vzhľadom na ich nízku úroveň.

3. Izolovanosť

Tu začínajú ťažkosti. Mnoho používateľov pracuje súčasne s jednou a tou istou databázou, mení tie isté údaje.

Situácia sa dá prirovnať k tomu, keď mnoho používateľov súčasne pracuje s jedným a tým istým repozitárom s kódmi a snaží sa naraz commitnúť zmeny v mnohých súboroch.

Databáza to všetko musí spracovávať v reálnom čase. Zohľadňujúc, že vo vážnych spoločnostiach existuje dokonca špeciálny človek, ktorý zodpovedá za kontrolu verzií (za zlučovanie vetiev, riešenie konfliktov a podobne), a DB by to všetko mala robiť v reálnom čase, je zjavná zložitost úlohy a správnosť návrhu databázy a kódu, ktorý ju obsluhuje.

Databáza nemôže pochopiť význam akcií vykonávaných používateľmi, aby sa predišlo konfliktom, ak pracujú na tých istých údajoch. Môže iba zrušiť jednu transakciu, ktorá je v rozpore s inou, alebo ich vykonať postupne.

Ďalší problém je, že počas vykonávania transakcie (pred potvrdením) môže byť stav databázy nekonzistentný, preto je žiaduce, aby ostatným transakciám nebol umožnený prístup k nekonzistentnému stavu databázy, čo sa dosahuje v relačných databázach mnohými spôsobmi: vytváraním snímok, mnohoversnými verziami riadkov atď.

Pri paralelnom vykonávaní transakcií je dôležité, aby si navzájom neprekážali. To je vlastnosť izolácie.

SQL definuje 4 úrovne izolácie:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

Preskúmame každú úroveň osobitne. Náklady na implementáciu každej úrovne rastú takmer exponenciálne.

READ UNCOMMITTED — to je najnižšia úroveň izolácie, ale zároveň najrýchlejšia. Transakcie môžu čítať zmeny vykonané navzájom.

READ COMMITTED — to je nasledujúca úroveň izolácie, ktorá je kompromisom. Transakcie nemôžu čítať zmeny vykonané navzájom až do potvrdenia, ale môžu čítať akékoľvek zmeny vykonané po potvrdení.

Ak máme dlhú transakciu T1, počas ktorej prebehli potvrdenia v transakciách T2, T3… Tn, ktoré pracovali s tými istými údajmi ako T1, pri požiadavke o údaje v T1 dostaneme zakaždým iný výsledok. Tento fenomén sa označuje ako nepovšimnuteľné čítanie.

REPEATABLE READ — na tejto úrovni izolácie nemáme fenomén nepovšimnuteľného čítania, pretože pre každý požiadavok na čítanie údajov sa vytvára snímka výsledných údajov a pri opätovnom použití v tej istej transakcii sa používajú údaje zo snímky. Avšak na tejto úrovni izolácie je možné čítať fantómové údaje. Tým sa myslí čítanie nových riadkov, ktoré boli pridané paralelnými potvrdenými transakciami.

SERIALIZABLE — najvyššia úroveň izolácie. Je charakterizovaná tým, že údaje nejakým spôsobom použité v transakcii (čítanie alebo zmena) sú prístupné iným transakciám až po dokončení prvej transakcie.

Na začiatok sa pozrime, či je izolácia operácií v transakcii od hlavného toku. Otvoríme 2 okná terminálu.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

Izolácia neexistuje. Jeden tok vidí, čo robí druhý, ktorý otvoril transakciu.

Pozrime sa, či vidia transakcie rôznych tokov to, čo sa deje v nich.

Otvoríme 2 okná terminálu a otvoríme 2 transakcie paralelne.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Paralelné transakcie vidia dáta navzájom. Takže sme dostali najjednoduchšiu, ale aj najrýchlejšiu úroveň izolácie READ UNCOMMITTED.

V podstate sa to dalo očakávať pre globály, pre ktoré sa výkon vždy kládol na prvé miesto.

Čo však robiť, ak potrebujeme vyššiu úroveň izolácie v operáciách s globálmi?

Tu je potrebné zamyslieť sa, prečo vlastne existujú úrovne izolácie a ako fungujú.

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

Toto môžeme dosiahnuť pomocou správnych zámkov v ObjectScripte, ktoré majú množstvo rôznych spôsobov použitia: môžeme vykonávať obyčajné, inkrementálne a viacnásobné zámky príkazom LOCK.

Nižšie úrovne izolácie sú kompromisy, ktoré majú za cieľ zvýšiť rýchlosť práce databázy.

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

Tento operátor umožňuje brať nielen exkluzívne zámky potrebné na zmenu dát, ale aj takzvané zdieľané, ktoré môžu brať naraz niekoľko tokov, keď potrebujú čítať dáta, ktoré by nemali byť menené inými procesmi počas čítania.

Viac informácií o dvojfázovej metóde zámkov v ruštine a angličtine:

→ Dvojfázový zámok
→ Two-phase locking

Zložitosť spočíva v tom, že počas transakcie môže byť stav databázy nekonzistentný, avšak tieto nekonzistentné dáta sú viditeľné inými procesmi. Ako tomu zabrániť?

Dosiahneme to pomocou zámkov takými oknami viditeľnosti, v ktorých bude stav databázy konzistentný. A všetky prístupy k takým oknám viditeľnosti konzistentného stavu budú kontrolované zámkami.

Zdieľané zámky tých istých údajov sú opakovateľné — môžu ich získať viaceré procesy. Tieto zámky zakazujú iným procesom meniť údaje, t. j. používajú sa na vytvorenie okien konzistentného stavu DB.

Exkluzívne zámky sa používajú na zmeny údajov — takú zámku môže získať iba jeden proces. Exkluzívnu zámku môže získať:

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

Transakcie v globáloch InterSystems IRIS

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

READ_COMMITTED — podstatou tejto úrovne je, že vidíme iba potvrdené údaje z iných tokov. Ak údaje v inej transakcii ešte nie sú potvrdené, vidíme ich predchádzajúcu verziu.

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

Bez špeciálnych obmien nebudeme môcť vidieť predchádzajúcu verziu údajov v IRIS, preto budeme musieť obísť zámky.

Tým pádom budeme musieť pomocou zdieľaných zámok povoliť čítanie údajov iba v momentoch konzistencie.

Predpokladajme, že máme databázu používateľov ^person, ktorí prevádzajú peniaze medzi sebou.

Moment prevodu od osoby 123 k osobe 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)

Moment žiadosti o množstvo peňazí od osoby 123 pred odpočtom by mal byť sprevádzaný exkluzívnou zámkou (predvolene):

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

A ak je potrebné zobraziť stav účtu v osobnej zóne, je možné použiť zdieľanú zámku alebo ju vôbec nepoužiť:

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

Ak však predpokladáme, že operácie prácu s DB sa vykonávajú prakticky okamžite (pripomínam, že globály — to sú oveľa nižšie štruktúry ako relačná tabuľka), potom potreba tejto úrovne klesá.

REPEATABLE READ — na tejto úrovni izolácie je dovolené, že môžu byť viaceré čítania údajov, ktoré môžu byť zmenené paralelnými transakciami.

Tým pádom budeme musieť zaviesť zdieľanú zámku na čítanie údajov, ktoré meníme, a exkluzívne zámky na údaje, ktoré meníme.

Operátor LOCK umožňuje podrobne zadefinovať všetky potrebné zámky v jednom operátore, ktorých môže byť veľmi veľa.

LOCK +^person(123, amount)#”S”
čítanie ^person(123, amount)

iné operácie (medzitým sa paralelné vlákna pokúšajú zmeniť ^person(123, amount), ale nemôžu)

LOCK +^person(123, amount)
zmena ^person(123, amount)
LOCK -^person(123, amount)

čítanie ^person(123, amount)
LOCK -^person(123, amount)#”S”

Pri vymenovávaní zámkov po čiarkach sú brané postupne, ale ak to urobíme takto:

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

sú brané atomárne všetky naraz.

SERIALIZE — budeme musieť nastaviť zámky tak, aby nakoniec všetky transakcie, ktoré majú spoločné dáta, boli vykonávané postupne. Pre tento prístup musia byť väčšina zámkov exkluzívne a brané na najmenšie oblasti globálu pre výkon.

Ak hovoríme o odpočítaní prostriedkov v globále ^person, pre ňu je akceptovateľná iba úroveň izolácie SERIALIZE, pretože peniaze musia byť míňané striktne postupne, inak je možné minúť tú istú sumu viackrát.

4. Dlhodobosť

Testoval som s tvrdým vypínaním kontajnera pomocou

docker kill my-iris

Databáza to prenášala dobre. Problémy neboli zistené.

Záver

Pre globály v InterSystems IRIS existuje podpora transakcií. Sú skutočne atomárne, spoľahlivé. Na zabezpečenie konzistencie DB v globáloch sú potrebné úsilie programátora a použitie transakcií, pretože v nej nie sú zložené vstavané konštrukcie ako vonkajšie kľúče.

Úroveň izolácie v globáloch bez použitia zámkov je READ UNCOMMITED, a pri použití zámkov je možné zaručiť až úroveň SERIALIZE.

Správnosť a rýchlosť práce transakcií v globáloch závisí veľmi silno od zručnosti programátora: čím viac sa používajú shared-zámky pri čítaní, tým vyššia je úroveň izolácie, a čím užšie sú brané exkluzívne zámky, tým väčšia je rýchlosť.

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster