Transakcije u InterSystems IRIS globalima

Transakcije u InterSystems IRIS globalimaInterSystems IRIS DBMS podržava zanimljive strukture za skladištenje podataka - globalne. U suštini, ovo su ključevi na više nivoa sa raznim dodatnim pogodnostima u obliku transakcija, brzim funkcijama za prelaženje stabala podataka, bravama i sopstvenim ObjectScript jezikom.

Više o globalima pročitajte u seriji članaka “Globali su blago-mačevi za pohranjivanje podataka”:

Drveće. Dio 1
Drveće. Dio 2
Retki nizovi. dio 3

Zanimalo me je kako se transakcije implementiraju u globalima, koje karakteristike postoje. Na kraju krajeva, ovo je potpuno drugačija struktura za pohranjivanje podataka od uobičajenih tablica. Mnogo niži nivo.

Kao što je poznato iz teorije relacionih baza podataka, dobra implementacija transakcija mora zadovoljiti zahtjeve ACID:

A - Atomski (atomičnost). Bilježe se sve promjene izvršene u transakciji ili nijedna.

C - Konzistentnost. Nakon što se transakcija završi, logičko stanje baze podataka mora biti interno konzistentno. Na mnogo načina ovaj zahtjev se odnosi na programera, ali u slučaju SQL baza podataka također se odnosi na strane ključeve.

Ja - Izolirati. Transakcije koje se odvijaju paralelno ne bi trebale uticati jedna na drugu.

D - Izdržljiv. Nakon uspješnog završetka transakcije, problemi na nižim nivoima (nestanak struje, na primjer) ne bi trebali utjecati na podatke izmijenjene transakcijom.

Globalne vrijednosti su nerelacijske strukture podataka. Dizajnirani su da rade super brzo na vrlo ograničenom hardveru. Pogledajmo implementaciju transakcija u globalima koristeći zvanična IRIS docker slika.

Za podršku transakcijama u IRIS-u, koriste se sljedeće naredbe: TSTART, TCOMMIT, TROLLBACK.

1. Atomičnost

Najlakši način za provjeru je atomičnost. Provjeravamo sa konzole baze podataka.

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

Zatim zaključujemo:

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

Dobijamo:

1 2 3

Sve je uredu. Atomičnost se održava: sve promjene se bilježe.

Zakomplikujmo zadatak, unesemo grešku i vidimo kako se transakcija pohranjuje, djelomično ili nikako.

Provjerimo još jednom atomičnost:

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

Zatim ćemo nasilno zaustaviti kontejner, pokrenuti ga i vidjeti.

docker kill my-iris

Ova komanda je skoro ekvivalentna prinudnom gašenju, jer šalje SIGKILL signal da se proces odmah zaustavi.

Možda je transakcija djelimično sačuvana?

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

- Ne, nije preživjelo.

Pokušajmo s naredbom rollback:

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)

Nista nije opstalo.

2. Dosljednost

Budući da se u bazama podataka baziranim na globalima, ključevi također prave na globalima (da vas podsjetim da je global struktura nižeg nivoa za pohranjivanje podataka od relacijske tablice), da bi se ispunio zahtjev konzistentnosti, promjena ključa mora biti uključena u istoj transakciji kao promjena u globalu.

Na primjer, imamo globalnu ^osobu, u kojoj pohranjujemo ličnosti i koristimo TIN kao ključ.

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

Da bismo imali brzu pretragu po prezimenu i imenu, napravili smo ključ ^index.

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

Da bi baza podataka bila konzistentna, moramo dodati personu ovako:

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

Shodno tome, prilikom brisanja moramo koristiti i transakciju:

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

Drugim riječima, ispunjavanje zahtjeva konzistentnosti u potpunosti leži na ramenima programera. Ali kada je reč o globalima, to je normalno, zbog njihove prirode niskog nivoa.

3. Izolacija

Ovdje počinje divljina. Mnogi korisnici istovremeno rade na istoj bazi podataka, mijenjajući iste podatke.

Situacija je uporediva sa onim kada mnogi korisnici istovremeno rade sa istim repozitorijumom koda i pokušavaju istovremeno da urezuju promene na više datoteka odjednom.

Baza podataka bi trebala sve to srediti u realnom vremenu. S obzirom da u ozbiljnim kompanijama postoji čak i posebna osoba koja je zadužena za kontrolu verzija (za spajanje grana, rješavanje konflikata i sl.), a baza podataka sve to mora raditi u realnom vremenu, složenost zadatka i ispravnost dizajn baze podataka i kod koji joj služi.

Baza podataka ne može razumjeti značenje radnji koje izvršavaju korisnici kako bi izbjegli sukobe ako rade na istim podacima. Može poništiti samo jednu transakciju koja je u sukobu s drugom ili ih izvršiti uzastopno.

Drugi problem je što tokom izvršavanja transakcije (prije urezivanja) stanje baze podataka može biti nekonzistentno, pa je poželjno da druge transakcije nemaju pristup nekonzistentnom stanju baze podataka, što se postiže u relacijskim bazama podataka. na mnogo načina: kreiranje snimaka, više verzija redova itd.

Prilikom paralelnog izvršavanja transakcija važno nam je da one ne ometaju jedna drugu. Ovo je svojstvo izolacije.

SQL definira 4 nivoa izolacije:

  • PROČITAJTE NEPORUČENO
  • PROČITANO OBVEZANO
  • ČITANJE ZA PONAVLJANJE
  • SERIALIZABLE

Pogledajmo svaki nivo posebno. Troškovi implementacije svakog nivoa rastu gotovo eksponencijalno.

PROČITAJTE NEPORUČENO - ovo je najniži nivo izolacije, ali ujedno i najbrži. Transakcije mogu čitati promjene koje su napravile jedna drugu.

PROČITANO OBVEZANO je sljedeći nivo izolacije, koji je kompromis. Transakcije ne mogu čitati međusobne promjene prije urezivanja, ali mogu čitati sve promjene napravljene nakon urezivanja.

Ako imamo dugu transakciju T1, tokom koje su se izvršila urezivanja u transakcijama T2, T3...Tn, koja je radila sa istim podacima kao i T1, onda ćemo pri traženju podataka u T1 svaki put dobiti drugačiji rezultat. Ovaj fenomen se naziva neponovljivo čitanje.

ČITANJE ZA PONAVLJANJE — u ovom nivou izolacije nemamo fenomen neponovljivog čitanja, zbog činjenice da se za svaki zahtjev za čitanje podataka kreira snimak rezultata rezultata i kada se ponovo koristi u istoj transakciji, podaci iz snimka se koristi. Međutim, moguće je čitati fantomske podatke na ovom nivou izolacije. Ovo se odnosi na čitanje novih redova koji su dodani paralelno predanim transakcijama.

SERIALIZABLE — najviši nivo izolacije. Karakterizira ga činjenica da podaci koji se koriste na bilo koji način u transakciji (čitanje ili mijenjanje) postaju dostupni drugim transakcijama tek nakon završetka prve transakcije.

Prvo, hajde da shvatimo da li postoji izolacija operacija u transakciji od glavne niti. Otvorimo 2 terminalska prozora.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

Nema izolacije. Jedna nit vidi šta radi druga koja je otvorila transakciju.

Hajde da vidimo da li transakcije različitih niti vide šta se dešava unutar njih.

Otvorimo 2 terminalska prozora i otvorimo 2 transakcije paralelno.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Paralelne transakcije vide međusobne podatke. Dakle, dobili smo najjednostavniji, ali i najbrži nivo izolacije, ČITAJ NEPORUKE.

U principu, to bi se moglo očekivati ​​za globalne kompanije, kojima je učinak uvijek bio prioritet.

Šta ako nam je potreban viši nivo izolacije u operacijama na globalima?

Ovdje morate razmisliti zašto su razine izolacije uopće potrebne i kako funkcioniraju.

Najviši nivo izolacije, SERIALIZE, znači da je rezultat transakcija koje se izvršavaju paralelno ekvivalentan njihovom sekvencijalnom izvršavanju, što garantuje odsustvo kolizija.

To možemo učiniti pomoću pametnih brava u ObjectScript-u, koje imaju mnogo različitih upotreba: možete izvršiti redovno, inkrementalno, višestruko zaključavanje pomoću naredbe LOCK.

Niži nivoi izolacije su kompromisi dizajnirani da povećaju brzinu baze podataka.

Hajde da vidimo kako možemo postići različite nivoe izolacije koristeći brave.

Ovaj operator vam omogućava da preuzmete ne samo ekskluzivna zaključavanja potrebna za promjenu podataka, već i takozvana dijeljena zaključavanja, koja mogu uzeti nekoliko niti paralelno kada trebaju pročitati podatke koje drugi procesi ne bi trebali mijenjati tokom procesa čitanja.

Više informacija o metodi dvofaznog blokiranja na ruskom i engleskom jeziku:

Dvofazno blokiranje
Dvofazno zaključavanje

Poteškoća je u tome što tokom transakcije stanje baze podataka može biti nekonzistentno, ali su ti nedosljedni podaci vidljivi drugim procesima. Kako to izbjeći?

Koristeći zaključavanje, kreiraćemo prozore vidljivosti u kojima će stanje baze podataka biti konzistentno. A sav pristup takvim prozorima vidljivosti dogovorenog stanja će biti kontroliran bravama.

Zajedničke brave na istim podacima mogu se ponovo koristiti – može ih uzeti nekoliko procesa. Ova zaključavanja sprječavaju druge procese da mijenjaju podatke, tj. koriste se za formiranje prozora konzistentnog stanja baze podataka.

Ekskluzivne brave se koriste za promjene podataka - samo jedan proces može uzeti takvo zaključavanje. Ekskluzivnu bravu mogu preuzeti:

  1. Bilo koji proces ako su podaci besplatni
  2. Samo proces koji ima zajedničko zaključavanje ovih podataka i koji je prvi zatražio ekskluzivno zaključavanje.

Transakcije u InterSystems IRIS globalima

Što je prozor vidljivosti uži, drugi procesi duže moraju da ga čekaju, ali stanje baze podataka unutar njega može biti konzistentnije.

READ_COMMITTED — suština ovog nivoa je da vidimo samo urezane podatke iz drugih niti. Ako podaci u drugoj transakciji još nisu predani, tada vidimo njihovu staru verziju.

Ovo nam omogućava da paraleliziramo rad umjesto da čekamo da se zaključavanje otpusti.

Bez posebnih trikova nećemo moći vidjeti staru verziju podataka u IRIS-u, pa ćemo se morati zadovoljiti bravama.

U skladu s tim, morat ćemo koristiti zajedničke brave kako bismo omogućili čitanje podataka samo u trenucima konzistentnosti.

Recimo da imamo korisničku bazu ^osobu koja prenosi novac jedni drugima.

Trenutak transfera od osobe 123 do 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)

Trenutak zahtjeva za iznos novca od osobe 123 prije zaduženja mora biti popraćen isključivim blokom (podrazumevano):

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

A ako trebate prikazati status računa na svom ličnom računu, tada možete koristiti zajedničko zaključavanje ili ga uopće ne koristiti:

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

Međutim, ako pretpostavimo da se operacije baze podataka izvode gotovo trenutno (da vas podsjetim da su globalne strukture mnogo niže razine od relacijske tablice), onda se potreba za ovim nivoom smanjuje.

ČITANJE ZA PONAVLJANJE - Ovaj nivo izolacije omogućava višestruko čitanje podataka koji se mogu modifikovati istovremenim transakcijama.

U skladu s tim, morat ćemo staviti zajedničko zaključavanje čitanja podataka koje mijenjamo i ekskluzivno zaključavanje podataka koje mijenjamo.

Na sreću, LOCK operator vam omogućava da u jednoj izjavi detaljno navedete sve potrebne brave, kojih može biti mnogo.

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

druge operacije (u ovom trenutku paralelne niti pokušavaju promijeniti ^person(123, iznos), ali ne mogu)

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

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

Kada navodite brave odvojene zarezima, one se uzimaju uzastopno, ali ako to učinite:

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

onda se uzimaju atomski odjednom.

SERIJALIZIRAJ — morat ćemo postaviti zaključavanja tako da se na kraju sve transakcije koje imaju zajedničke podatke izvršavaju sekvencijalno. Za ovaj pristup, većina brava bi trebala biti ekskluzivna i preuzeti na najmanjim područjima globalnog radi performansi.

Ako govorimo o zaduživanju sredstava u globalnoj ^osobi, onda je za nju prihvatljiv samo nivo izolacije SERIALIZE, jer se novac mora trošiti striktno sekvencijalno, inače je moguće potrošiti isti iznos više puta.

4. Trajnost

Proveo sam testove sa tvrdim rezanjem kontejnera pomoću

docker kill my-iris

Baza ih je dobro podnosila. Nisu identifikovani nikakvi problemi.

zaključak

Za globalne, InterSystems IRIS ima podršku za transakcije. Oni su zaista atomski i pouzdani. Da bi se osigurala konzistentnost baze podataka zasnovana na globalima, potrebni su napori programera i upotreba transakcija, budući da ona nema složene ugrađene konstrukcije kao što su strani ključevi.

Nivo izolacije globala bez korištenja brava je READ UNCOMMITED, a kada se koriste brave može se osigurati do nivoa SERIALIZE.

Ispravnost i brzina transakcija na globalima umnogome ovisi o vještini programera: što se više dijeljene brave koriste prilikom čitanja, to je viši nivo izolacije i što se više usko isključivih zaključavanja uzimaju, to su performanse brže.

izvor: www.habr.com

Dodajte komentar