Transakcije u InterSystems IRIS globalima

Transakcije u InterSystems IRIS globalimaInterSystems IRIS DBMS podržava zanimljive strukture pohrane podataka koje se nazivaju globali. To su u biti višerazinski ključevi s raznim dodatnim značajkama kao što su transakcije, brze funkcije za prolazak kroz podatkovna stabla, brave i vlasnički ObjectScript jezik.

Više o globalima pročitajte u seriji članaka "Globalni objekti - mačevi pohrane podataka":

Drveće. 1. dio
Drveće. 2. dio
Rijetki nizovi. 3. dio

Zanimalo me kako su transakcije implementirane u globalnim tablicama i koje su njihove osobitosti. Uostalom, to je potpuno drugačija struktura pohrane podataka od poznatih tablica. Mnogo je niže razine.

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

A — Atomski (atomnost). Sve promjene napravljene u transakciji se bilježe ili se uopće ne bilježe.

C — Konzistentnost. Nakon što je transakcija završena, logičko stanje baze podataka mora biti interno konzistentno. Ovaj zahtjev se uvelike odnosi na programera, ali u slučaju SQL baza podataka odnosi se i na strane ključeve.

Ja — Izolirati (izolacija). Istovremeno izvođenje transakcija ne bi smjelo utjecati jedna na drugu.

D - Izdržljiv. Nakon što je transakcija uspješno završena, problemi na nižim razinama (npr. nestanak struje) ne bi trebali utjecati na podatke koje je transakcija modificirala.

Globalne su nerelacijske strukture podataka. Dizajnirane su za iznimno brz rad na vrlo ograničenom hardveru. Pogledajmo implementaciju transakcija u globalnima pomoću Službena slika IRIS Dockera.

Za podršku transakcijama u IRIS-u koriste se sljedeće naredbe: POČETAK, TCOMMIT, POVRATAK.

1. Atomnost

Najlakši način za provjeru atomičnosti je iz konzole baze podataka.

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

Zatim donosimo zaključak:

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

Dobivamo:

1 2 3

Sve je u redu. Atomicnost je očuvana: sve promjene su zapisane.

Zakomplicirajmo zadatak, uvedimo grešku i vidimo kako se transakcija sprema, djelomično ili uopće ne.

Provjerimo još jednom atomičnost:

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

Nakon toga, prisilno ćemo zaustaviti kontejner, pokrenuti ga i vidjeti.

docker kill my-iris

Ova naredba je gotovo ekvivalentna prisilnom isključenju, jer šalje SIGKILL signal za trenutno zaustavljanje procesa.

Možda je transakcija djelomično spremljena?

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

- Ne, nije preživjelo.

Pokušajmo s naredbom za vraćanje u prethodno stanje:

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šta također nije preživjelo.

2. Dosljednost

Budući da se u globalno temeljenim bazama podataka ključevi također izrađuju na globalima (imajte na umu da je global struktura za pohranu podataka niže razine od relacijske tablice), kako bi se zadovoljio zahtjev konzistentnosti, promjena ključa mora biti uključena u istu transakciju kao i globalna promjena.

Na primjer, imamo globalni ^person u kojem pohranjujemo osobne podatke i koristimo TIN kao ključ.

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

Kako bismo omogućili brzo pretraživanje po prezimenu i imenu, kreirali smo ključ ^index.

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

Da bi baza podataka bila konzistentna, moramo dodati osoblje na sljedeći način:

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

Sukladno tome, prilikom brisanja moramo koristiti i transakciju:

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

Drugim riječima, odgovornost za osiguravanje konzistentnosti u potpunosti leži na programeru. Ali kada su u pitanju globalne promjenjive, to je normalno zbog njihove niskorazinske prirode.

3. Izolacija

Tu počinje složenost. Mnogi korisnici istovremeno rade na istoj bazi podataka, mijenjajući iste podatke.

Situacija je usporediva s onom kada mnogi korisnici istovremeno rade s istim repozitorijem koda i pokušavaju istovremeno pohraniti promjene u više datoteka.

Baza podataka mora sve to obraditi u stvarnom vremenu. Uzimajući u obzir da ozbiljne tvrtke čak imaju i namjensku osobu odgovornu za kontrolu verzija (spajanje grana, rješavanje sukoba itd.), a baza podataka mora sve to obraditi u stvarnom vremenu, složenost zadatka i važnost pravilnog dizajniranja baze podataka i koda koji je podržava postaju jasna.

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

Drugi problem je što tijekom izvršavanja transakcije (prije commita), stanje baze podataka može biti nekonzistentno, pa je poželjno da druge transakcije nemaju pristup nekonzistentnom stanju baze podataka, što se u relacijskim bazama podataka postiže na mnogo načina: stvaranjem snapshot-ova, višestrukim verzijama redaka itd.

Prilikom paralelnog izvršavanja transakcija važno je da se ne miješaju jedna s drugom. To je svojstvo izolacije.

SQL definira 4 razine izolacije:

  • ČITAJ NEOBVEZENO
  • PROČITANO POTVRĐENO
  • PONOVLJIVO ČITANJE
  • SERIJALIZABILNO

Pogledajmo svaku razinu zasebno. Troškovi implementacije svake razine rastu gotovo eksponencijalno.

ČITAJ NEOBVEZENO — Ovo je najniža razina izolacije, ali i najbrža. Transakcije mogu čitati međusobne promjene.

PROČITANO POTVRĐENO — Ovo je sljedeća razina izolacije, što je kompromis. Transakcije ne mogu čitati međusobne promjene prije commita, ali mogu čitati sve promjene napravljene nakon commita.

Ako imamo dugotrajnu transakciju T1, tijekom koje su izvršene izmjene u transakcijama T2, T3 i Tn, koje su radile s istim podacima kao i T1, tada ćemo prilikom upita podataka u T1 svaki put dobiti drugačiji rezultat. Taj se fenomen naziva neponovljiva čitanja.

PONOVLJIVO ČITANJE — Na ovoj razini izolacije nemamo fenomen neponovljivih čitanja, jer se za svaki zahtjev za čitanje stvara snimka rezultirajućih podataka, a kada se ponovno koriste u istoj transakciji, koriste se podaci iz snimke. Međutim, na ovoj razini izolacije moguće je čitanje fantomskih podataka. To se odnosi na čitanje novih redaka koji su dodani istodobnim potvrđenim transakcijama.

SERIJALIZABILNO — najviša razina izolacije. Karakterizira je činjenica da podaci korišteni u transakciji (pročitani ili izmijenjeni) postaju dostupni drugim transakcijama tek nakon što je prva transakcija završena.

Prvo, provjerimo jesu li operacije u transakciji izolirane od glavne niti. Otvorimo dva prozora terminala.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

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

Da vidimo mogu li transakcije u različitim nitima vidjeti što se događa unutar njih.

Otvorimo 2 terminalna prozora i paralelno otvorimo 2 transakcije.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Istodobne transakcije mogu vidjeti podatke jedna drugoj. Dakle, imamo najjednostavniju, ali i najbržu razinu izolacije: READ UNCOMMITTED.

U načelu, to se može očekivati ​​za globalne tvrtke, kojima su performanse oduvijek bile glavni prioritet.

Što ako nam je potrebna viša razina izolacije u globalnim operacijama?

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

Najviša razina izolacije, SERIALIZE, znači da je rezultat transakcija izvršenih paralelno ekvivalentan njihovom sekvencijalnom izvršavanju, što jamči odsutnost kolizija.

To možemo učiniti uz pomoć kompetentnih zaključavanja u ObjectScriptu, koja imaju mnogo različitih načina primjene: možete napraviti redovno, inkrementalno, višestruko zaključavanje naredbom LOCK.

Niže razine izolacije su kompromisi osmišljeni za poboljšanje performansi baze podataka.

Pogledajmo kako možemo postići različite razine izolacije pomoću brava.

Ovaj operator vam omogućuje ne samo korištenje ekskluzivnih brava potrebnih za promjenu podataka, već i takozvanih dijeljenih brava, koje nekoliko niti može istovremeno koristiti paralelno kada trebaju čitati podatke koje drugi procesi ne bi trebali mijenjati tijekom procesa čitanja.

Saznajte više o metodi dvofaznog blokiranja na ruskom i engleskom jeziku:

Dvofazno blokiranje
Dvofazno zaključavanje

Problem je u tome što tijekom transakcije stanje baze podataka može biti nekonzistentno, ali ti nekonzistentni podaci su vidljivi drugim procesima. Kako se to može izbjeći?

Koristit ćemo brave za stvaranje prozora vidljivosti unutar kojih će stanje baze podataka biti konzistentno. Sav pristup tim prozorima vidljivosti konzistentnog stanja bit će kontroliran bravama.

Dijeljene brave na istim podacima mogu se ponovno koristiti - mogu ih preuzeti više procesa. Ove brave sprječavaju druge procese da mijenjaju podatke, što znači da se koriste za stvaranje prozora konzistentnog stanja baze podataka.

Isključive brave se koriste za izmjenu podataka - samo jedan proces može dobiti takvu bravu. Isključiva brava se može dobiti:

  1. Bilo koji proces ako su podaci slobodni
  2. Samo proces koji ima dijeljenu bravu na ovim podacima i prvi je zatražio ekskluzivnu bravu.

Transakcije u InterSystems IRIS globalima

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

PROČITANO_POTVRĐENO — bit ove razine je da vidimo samo potvrđene podatke iz drugih niti. Ako podaci u drugoj transakciji još nisu potvrđeni, tada vidimo njihovu staru verziju.

To nam omogućuje paralelizaciju rada umjesto čekanja da se otključavanje otključa.

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

Sukladno tome, morat ćemo koristiti zajedničke brave kako bismo omogućili čitanje podataka samo u trenucima konzistentnosti.

Recimo da imamo korisničku bazu ^osoba koje međusobno prebacuju novac.

Trenutak prijenosa s osobe 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)

Trenutak traženja iznosa novca od osobe 123 prije otpisa mora biti popraćen ekskluzivnom bravom (podrazumijeva se):

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

Ako trebate prikazati stanje na svom osobnom računu, 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 s bazom podataka izvode gotovo trenutno (podsjećam vas da su globalne strukture puno niže razine od relacijske tablice), tada se potreba za ovom razinom smanjuje.

PONOVLJIVO ČITANJE — Ova razina izolacije omogućuje višestruko čitanje podataka koji se mogu mijenjati istodobnim transakcijama.

Sukladno tome, morat ćemo postaviti zajedničku bravu na čitanje podataka koje mijenjamo i ekskluzivne brave na podatke koje mijenjamo.

Srećom, operator LOCK omogućuje vam detaljan popis svih potrebnih brava, kojih može biti puno, u jednom operatoru.

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

ostale operacije (trenutno, paralelne niti pokušavaju promijeniti ^person(123, amount), ali ne mogu)

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

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

Prilikom navođenja brava odvojenih zarezima, one se uzimaju sekvencijalno, i ako to učinite:

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

zatim se uzimaju atomski svi odjednom.

SERIALIZIRAJ — Morat ćemo postaviti brave tako da se sve transakcije koje dijele podatke u konačnici izvršavaju sekvencijalno. Za ovaj pristup, većina brava trebala bi biti ekskluzivna i primjenjivati ​​se na najmanjim regijama globala iz razloga performansi.

Ako govorimo o otpisima sredstava u ^person globalu, onda je za to prihvatljiva samo razina izolacije SERIALIZE, budući da se novac mora trošiti strogo sekvencijalno, inače je moguće potrošiti isti iznos nekoliko puta.

4. Trajnost

Proveo sam testove s tvrdim rezanjem spremnika pomoću

docker kill my-iris

Baza ih je dobro podnijela. Nisu se pojavili nikakvi problemi.

Zaključak

InterSystems IRIS podržava transakcije za globalne vrijednosti. One su zaista atomske i pouzdane. Osiguravanje konzistentnosti baze podataka s globalnim vrijednostima zahtijeva napor programera i korištenje transakcija, jer nedostaju složene ugrađene konstrukcije poput stranih ključeva.

Razina izolacije za globalne promjene bez korištenja brava je READ UNCOMMITTED, a s bravama se može postići do razine SERIALIZE.

Ispravnost i brzina transakcija na globalnim vrijednostima uvelike ovisi o vještini programera: što se više dijeljenih brava koristi za čitanje, to je viša razina izolacije, a što se više koriste usko ekskluzivne brave, to su veće performanse.

Izvor: www.habr.com

Dodajte komentar