Transakcije u InterSystems IRIS globalima

Transakcije u InterSystems IRIS globalimaInterSystems IRIS DBMS podržava zanimljive strukture za pohranu podataka - globale. U biti, to su višerazinski ključevi s raznim dodatnim dodacima u obliku transakcija, brzih funkcija za obilaženje podatkovnih stabala, brava i vlastitog ObjectScript jezika.

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

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

Zanimalo me kako se transakcije implementiraju u globalima, koje mogućnosti postoje. Uostalom, ovo je potpuno drugačija struktura za pohranu podataka od uobičajenih tablica. Mnogo niža razina.

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

A - Atomski (atomarnost). Bilježe se sve promjene napravljene u transakciji ili nijedna.

C - Dosljednost. Nakon dovršetka transakcije, logično stanje baze podataka mora biti interno dosljedno. Na mnoge načine ovaj se zahtjev odnosi na programera, ali u slučaju SQL baza podataka odnosi se i na strane ključeve.

Ja - Izolirati. Transakcije koje se izvode paralelno ne bi trebale utjecati jedna na drugu.

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

Globali su nerelacijske strukture podataka. Dizajnirani su da rade super brzo na vrlo ograničenom hardveru. Pogledajmo implementaciju transakcija u globalima pomoću službena IRIS docker slika.

Za podršku transakcijama u IRIS-u koriste se sljedeće naredbe: TPOČNI, TCOMMIT, TROLLACK.

1. Atomičnost

Najlakši način provjere je atomičnost. Provjeravamo iz 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)

Dobivamo:

1 2 3

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

Idemo zakomplicirati zadatak, uvesti pogrešku i vidjeti kako je transakcija spremljena, djelomično ili nikako.

Provjerimo ponovno atomičnost:

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

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

docker kill my-iris

Ova je naredba gotovo jednaka prisilnom gašenju, budući da šalje signal SIGKILL za trenutno zaustavljanje procesa.

Možda je transakcija djelomično spremljena?

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

- Ne, nije preživio.

Pokušajmo s naredbom vraćanja:

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)

Također ništa nije sačuvano.

2. Dosljednost

Budući da se u bazama podataka temeljenim na globalima, ključevi također izrađuju na globalima (dopustite mi da vas podsjetim da je global struktura niže razine za pohranjivanje podataka od relacijske tablice), kako bi se ispunio zahtjev dosljednosti, promjena ključa mora biti uključena u istoj transakciji kao promjena u globalu.

Na primjer, imamo globalnu ^osobu, u koju spremamo osobnosti i koristimo TIN kao ključ.

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

Za brzu pretragu po prezimenu i imenu napravili smo tipku ^index.

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

Kako bi baza podataka bila dosljedna, moramo dodati personu ovako:

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

Sukladno tome, prilikom brisanja također moramo koristiti transakciju:

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

Drugim riječima, ispunjavanje zahtjeva dosljednosti u potpunosti leži na plećima programera. Ali kada je riječ o globalima, to je normalno zbog njihove prirode niske razine.

3. Izolacija

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

Situacija je usporediva s onom kada mnogi korisnici istovremeno rade s istim spremištem koda i pokušavaju istovremeno unijeti promjene u više datoteka odjednom.

Baza bi to sve trebala srediti u stvarnom vremenu. S obzirom da u ozbiljnim tvrtkama postoji čak i posebna osoba koja je zadužena za kontrolu verzija (za spajanje grana, rješavanje sukoba i sl.), a baza 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 izvode 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 tijekom izvođenja transakcije (prije komitiranja) stanje baze podataka može biti nedosljedno, pa je poželjno da druge transakcije nemaju pristup nekonzistentnom stanju baze podataka, što se postiže u relacijskim bazama podataka. na mnoge načine: stvaranje snimki, više verzija redaka itd.

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

SQL definira 4 razine izolacije:

  • ČITAJ NEOBAVEZANO
  • PROČITAJ PREDANO
  • PONOVNO ČITANJE
  • SERIJALIZIVO

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

ČITAJ NEOBAVEZANO - ovo je najniža razina izolacije, ali ujedno i najbrža. Transakcije mogu čitati izmjene koje su napravile jedna druga.

PROČITAJ PREDANO je sljedeća razina izolacije, koja je kompromis. Transakcije ne mogu čitati međusobne promjene prije predaje, ali mogu pročitati sve promjene napravljene nakon predaje.

Ako imamo dugu transakciju T1, tijekom koje su se komitovi odvijali u transakcijama T2, T3 ... Tn, koje su radile s istim podacima kao i T1, tada ćemo prilikom traženja podataka u T1 svaki put dobiti drugačiji rezultat. Taj se fenomen naziva neponovljivo čitanje.

PONOVNO ČITANJE — na ovoj razini izolacije nemamo fenomen neponovljivog čitanja, zbog činjenice da se za svaki zahtjev za čitanje podataka stvara snimka podataka rezultata i kada se ponovno koriste u istoj transakciji, podaci iz snimke koristi se. Međutim, moguće je čitati fantomske podatke na ovoj razini izolacije. Ovo se odnosi na čitanje novih redaka koji su dodani paralelno predanim transakcijama.

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

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

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

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

Pogledajmo vide li transakcije različitih niti što se događa unutar njih.

Otvorimo 2 prozora terminala i otvorimo 2 transakcije paralelno.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Paralelne transakcije međusobno vide podatke. Dakle, dobili smo najjednostavniju, ali i najbržu razinu izolacije, ČITAJ UNCOMMITED.

U principu, to se moglo očekivati ​​za globale, kojima je performansa uvijek bila prioritet.

Što ako trebamo višu razinu izolacije u operacijama na globalima?

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

Najviša razina izolacije, SERIALIZE, znači da je rezultat transakcija koje se izvode paralelno jednak njihovom sekvencijalnom izvršavanju, što jamči nepostojanje kolizija.

To možemo učiniti korištenjem pametnih zaključavanja u ObjectScriptu, koja imaju puno različitih namjena: možete napraviti redovno, inkrementalno, višestruko zaključavanje s naredbom LOCK.

Niže razine izolacije su kompromisi osmišljeni za povećanje brzine baze podataka.

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

Ovaj vam operator omogućuje ne samo isključiva zaključavanja potrebna za promjenu podataka, već i takozvana zajednička zaključavanja, koja mogu uzeti nekoliko niti paralelno kada trebaju čitati podatke koje ne bi trebali mijenjati drugi procesi tijekom 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 tijekom transakcije stanje baze podataka može biti nedosljedno, ali su ti nedosljedni podaci vidljivi drugim procesima. Kako to izbjeći?

Pomoću zaključavanja stvorit ćemo prozore vidljivosti u kojima će stanje baze biti konzistentno. I svi pristupi takvim prozorima vidljivosti dogovorenog stanja bit će kontrolirani bravama.

Zajednička zaključavanja istih podataka mogu se višekratno koristiti—može ih preuzeti nekoliko procesa. Ove brave sprječavaju druge procese da mijenjaju podatke, tj. koriste se za formiranje prozora dosljednog stanja baze podataka.

Ekskluzivna zaključavanja koriste se za promjene podataka - samo jedan proces može preuzeti takvo zaključavanje. Ekskluzivnu bravu može preuzeti:

  1. Svaki 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 moraju duže čekati na njega, ali stanje baze podataka unutar njega može biti dosljednije.

READ_COMMITTED — bit ove razine je da vidimo samo predane podatke iz drugih niti. Ako podaci u drugoj transakciji još nisu predani, tada vidimo njihovu staru verziju.

To nam omogućuje da paraleliziramo rad umjesto da čekamo da se otključa.

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

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

Recimo da imamo bazu korisnika ^osobe koje međusobno prenose novac.

Trenutak prelaska sa 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 novčanog iznosa od osobe 123 prije terećenja mora biti popraćen ekskluzivnom blokadom (standardno):

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

A ako trebate prikazati status računa na svom osobnom računu, tada možete koristiti zajedničku bravu ili je uopće ne koristiti:

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

Međutim, ako pretpostavimo da se operacije baze podataka izvode gotovo trenutačno (dopustite mi da vas podsjetim da su globalne strukture mnogo niže razine od relacijske tablice), tada se potreba za ovom razinom smanjuje.

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

Sukladno tome, morat ćemo staviti zajedničko zaključavanje čitanja podataka koje mijenjamo i ekskluzivno zaključavanje podataka koje mijenjamo.

Srećom, operator LOCK vam omogućuje da detaljno navedete sve potrebne brave, kojih može biti puno, u jednoj izjavi.

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

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

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

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

Prilikom popisa zaključavanja odvojenih zarezima, uzimaju se uzastopno, ali ako učinite ovo:

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

tada se uzimaju atomski svi odjednom.

SERIJALIZIRATI — morat ćemo postaviti zaključavanja tako da se u konačnici sve transakcije koje imaju zajedničke podatke izvršavaju uzastopno. Za ovaj pristup, većina zaključavanja trebala bi biti isključiva i preuzeti na najmanjim područjima globalne izvedbe.

Ako govorimo o zaduženju sredstava u globalnoj ^ osobi, tada je za njega 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 posude pomoću

docker kill my-iris

Baza ih je dobro podnosila. Nisu identificirani nikakvi problemi.

Zaključak

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

Razina izolacije globala bez korištenja zaključavanja je READ UNCOMMITED, a kod korištenja zaključavanja može se osigurati do razine SERIALIZE.

Ispravnost i brzina transakcija na globalima uvelike ovisi o vještini programera: što se više zajedničkih zaključavanja koriste prilikom čitanja, to je viša razina izolacije, a što se uzimaju usko isključiva zaključavanja, to je izvedba brža.

Izvor: www.habr.com

Dodajte komentar