Transakce v InterSystems IRIS globals

Transakce v InterSystems IRIS globalsInterSystems IRIS DBMS podporuje zajímavé struktury pro ukládání dat - globals. V podstatě se jedná o víceúrovňové klíče s různými doplňkovými vychytávkami v podobě transakcí, rychlých funkcí pro procházení datových stromů, zámků a vlastního jazyka ObjectScript.

Přečtěte si více o globalech v sérii článků „Globals jsou meče pokladů pro ukládání dat“:

Stromy. Část 1
Stromy. Část 2
Řídká pole. Část 3

Začal jsem se zajímat o to, jak jsou transakce implementovány v globalech, jaké tam jsou funkce. Přeci jen se jedná o úplně jinou strukturu pro ukládání dat než u běžných tabulek. Mnohem nižší úroveň.

Jak je známo z teorie relačních databází, dobrá implementace transakcí musí splňovat požadavky ACID:

A - Atomová (atomická). Zaznamenají se všechny změny provedené v transakci nebo žádné změny.

C - Konzistence. Po dokončení transakce musí být logický stav databáze vnitřně konzistentní. Tento požadavek se v mnoha ohledech týká programátora, ale v případě SQL databází i cizích klíčů.

Já - Izolovat. Souběžně probíhající transakce by se neměly vzájemně ovlivňovat.

D - Odolné. Po úspěšném dokončení transakce by problémy na nižších úrovních (například výpadek napájení) neměly ovlivnit data změněná transakcí.

Globální jsou nerelační datové struktury. Byly navrženy tak, aby běžely velmi rychle na velmi omezeném hardwaru. Podívejme se na implementaci transakcí v globalech pomocí oficiální obrázek doku IRIS.

Pro podporu transakcí v IRIS se používají následující příkazy: TSTART, TCOMMIT, TROLLBACK.

1. Atomicita

Nejjednodušší způsob kontroly je atomicita. Kontrolujeme z konzole databáze.

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

Pak docházíme k závěru:

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

Dostaneme:

1 2 3

Vše je v pořádku. Atomicita je zachována: všechny změny jsou zaznamenány.

Pojďme si úkol zkomplikovat, zavést chybu a podívat se, jak se transakce uloží, částečně nebo vůbec.

Znovu zkontrolujeme atomicitu:

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

Pak nádobu násilně zastavíme, spustíme a uvidíme.

docker kill my-iris

Tento příkaz je téměř ekvivalentní nucenému vypnutí, protože vysílá signál SIGKILL k okamžitému zastavení procesu.

Možná byla transakce částečně zachráněna?

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

- Ne, nepřežilo.

Zkusme příkaz 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)

Nic se také nedochovalo.

2. Konzistence

Vzhledem k tomu, že v databázích založených na globálech se klíče vyrábějí také na globálech (připomínám, že globální je struktura nižší úrovně pro ukládání dat než relační tabulka), pro splnění požadavku na konzistenci je třeba zahrnout změnu klíče. ve stejné transakci jako změna v globálním.

Máme například globální ^person, ve kterém ukládáme osobnosti a jako klíč používáme TIN.

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

Aby bylo možné rychle vyhledávat podle příjmení a jména, vytvořili jsme klíč ^index.

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

Aby byla databáze konzistentní, musíme personu přidat takto:

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

V souladu s tím musíme při mazání použít také transakci:

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

Jinými slovy, splnění požadavku konzistence spočívá výhradně na programátorových bedrech. Ale pokud jde o globály, je to normální kvůli jejich nízkoúrovňové povaze.

3. Izolace

Tady začíná divočina. Mnoho uživatelů současně pracuje na stejné databázi a mění stejná data.

Situace je srovnatelná s tím, když mnoho uživatelů současně pracuje se stejným úložištěm kódu a pokouší se současně odevzdat změny do mnoha souborů najednou.

Databáze by to měla vyřešit v reálném čase. Vzhledem k tomu, že ve seriózních firmách existuje i speciální osoba, která je zodpovědná za správu verzí (za slučování poboček, řešení konfliktů atd.), a to vše musí databáze dělat v reálném čase, je složitost úkolu a správnost návrh databáze a kód, který ji obsluhuje.

Databáze nemůže pochopit význam akcí prováděných uživateli, aby se vyhnula konfliktům, pokud pracují se stejnými daty. Může vrátit zpět pouze jednu transakci, která je v konfliktu s jinou, nebo je provést postupně.

Dalším problémem je, že během provádění transakce (před potvrzením) může být stav databáze nekonzistentní, proto je žádoucí, aby ostatní transakce neměly přístup k nekonzistentnímu stavu databáze, čehož je dosahováno u relačních databází. mnoha způsoby: vytváření snímků, více verzí řádků atd.

Při paralelním provádění transakcí je pro nás důležité, aby se vzájemně nerušily. Toto je vlastnost izolace.

SQL definuje 4 úrovně izolace:

  • PŘEČTĚTE SI NENÍ DOPORUČENO
  • PŘEČTĚTE SI POVINNÉ
  • OPAKOVATELNÉ ČTĚNÍ
  • SERIALIZAČNÍ

Podívejme se na každou úroveň zvlášť. Náklady na implementaci každé úrovně rostou téměř exponenciálně.

PŘEČTĚTE SI NENÍ DOPORUČENO - to je nejnižší úroveň izolace, ale zároveň nejrychlejší. Transakce mohou číst změny provedené navzájem.

PŘEČTĚTE SI POVINNÉ je další úroveň izolace, která je kompromisem. Transakce si nemohou vzájemně číst změny před odevzdáním, ale mohou číst jakékoli změny provedené po odevzdání.

Pokud máme dlouhou transakci T1, během které proběhly commity v transakcích T2, T3 ... Tn, které pracovaly se stejnými daty jako T1, tak při požadavku na data v T1 dostaneme pokaždé jiný výsledek. Tento jev se nazývá neopakovatelné čtení.

OPAKOVATELNÉ ČTĚNÍ — na této úrovni izolace nedochází k fenoménu neopakovatelného čtení, protože pro každý požadavek na čtení dat je vytvořen snímek výsledných dat a při opětovném použití ve stejné transakci data ze snímku se používá. Je však možné číst fantomová data na této úrovni izolace. To se týká čtení nových řádků, které byly přidány paralelními potvrzenými transakcemi.

SERIALIZAČNÍ — nejvyšší úroveň izolace. Vyznačuje se tím, že data použitá jakýmkoliv způsobem v transakci (čtení nebo změna) jsou dostupná pro další transakce až po dokončení první transakce.

Nejprve zjistíme, zda existuje izolace operací v transakci od hlavního vlákna. Otevřeme 2 okna terminálu.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

Neexistuje žádná izolace. Jedno vlákno vidí, co dělá druhý, který transakci otevřel.

Podívejme se, zda transakce různých vláken vidí, co se v nich děje.

Otevřeme 2 terminálová okna a otevřeme 2 transakce paralelně.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Paralelní transakce vzájemně vidí svá data. Dostali jsme tedy nejjednodušší, ale také nejrychlejší úroveň izolace, NEZÁVAZNĚ PŘEČTĚTE.

V zásadě by se to dalo očekávat u globalů, pro které byl výkon vždy prioritou.

Co když potřebujeme vyšší úroveň izolace v operacích na globálech?

Zde je třeba se zamyslet nad tím, proč jsou úrovně izolace vůbec potřeba a jak fungují.

Nejvyšší úroveň izolace SERIALIZE znamená, že výsledek paralelně prováděných transakcí je ekvivalentní jejich sekvenčnímu provádění, což zaručuje absenci kolizí.

Můžeme to udělat pomocí inteligentních zámků v ObjectScript, které mají mnoho různých použití: můžete provádět pravidelné, přírůstkové, vícenásobné zamykání pomocí příkazu LOCK.

Nižší úrovně izolace jsou kompromisy navržené ke zvýšení rychlosti databáze.

Podívejme se, jak můžeme dosáhnout různých úrovní izolace pomocí zámků.

Tento operátor umožňuje převzít nejen exkluzivní zámky potřebné pro změnu dat, ale také tzv. sdílené zámky, které mohou zabírat několik vláken paralelně, když potřebují číst data, která by neměla být změněna jinými procesy během procesu čtení.

Další informace o metodě dvoufázového blokování v ruštině a angličtině:

Dvoufázové blokování
Dvoufázové zamykání

Potíž je v tom, že během transakce může být stav databáze nekonzistentní, ale tato nekonzistentní data jsou viditelná pro ostatní procesy. Jak se tomu vyhnout?

Pomocí zámků vytvoříme okna viditelnosti, ve kterých bude stav databáze konzistentní. A veškerý přístup do takových oken viditelnosti dohodnutého stavu bude řízen zámky.

Sdílené zámky na stejných datech jsou opakovaně použitelné – může je převzít několik procesů. Tyto zámky brání jiným procesům měnit data, tzn. používají se k vytvoření oken konzistentního stavu databáze.

Pro změny dat se používají exkluzivní zámky - takový zámek může vzít pouze jeden proces. Exkluzivní zámek může získat:

  1. Jakýkoli proces, pokud jsou data volná
  2. Pouze proces, který má sdílený zámek na tato data a jako první požádal o exkluzivní zámek.

Transakce v InterSystems IRIS globals

Čím užší je okno viditelnosti, tím déle na něj musí ostatní procesy čekat, ale o to konzistentnější může být stav databáze v něm.

READ_COMMITTED — podstatou této úrovně je, že vidíme pouze potvrzená data z jiných vláken. Pokud data v jiné transakci ještě nebyla potvrzena, vidíme její starou verzi.

To nám umožňuje paralelizovat práci místo čekání na uvolnění zámku.

Bez speciálních triků neuvidíme starou verzi dat v IRIS, takže si budeme muset vystačit se zámky.

V souladu s tím budeme muset používat sdílené zámky, abychom umožnili čtení dat pouze v momentech konzistence.

Řekněme, že máme uživatelskou základnu ^osobu, která si navzájem převádí peníze.

Okamžik převodu 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žádání peněžní částky od osoby 123 před odečtením musí být doprovázeno výhradním blokováním (standardně):

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

A pokud potřebujete zobrazit stav účtu ve svém osobním účtu, můžete použít sdílený zámek nebo jej nepoužívat vůbec:

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

Pokud však předpokládáme, že databázové operace jsou prováděny téměř okamžitě (dovolte mi připomenout, že globals jsou strukturou mnohem nižší úrovně než relační tabulka), pak potřeba této úrovně klesá.

OPAKOVATELNÉ ČTĚNÍ - Tato úroveň izolace umožňuje vícenásobné čtení dat, které lze upravit souběžnými transakcemi.

V souladu s tím budeme muset dát sdílený zámek na čtení dat, která měníme, a výhradní zámky na data, která měníme.

Naštěstí operátor LOCK umožňuje v jednom výpisu podrobně vypsat všechny potřebné zámky, kterých může být opravdu hodně.

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

další operace (v tuto chvíli se paralelní vlákna snaží změnit ^person(123, množství), ale nemohou)

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

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

Při výpisu zámků oddělených čárkami se berou postupně, ale pokud to uděláte:

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

pak se vezmou atomicky všechny najednou.

SERIALIZUJTE — budeme muset nastavit zámky, aby se nakonec všechny transakce, které mají společná data, prováděly postupně. Pro tento přístup by většina zámků měla být exkluzivní a měla by být použita na nejmenších oblastech globálního výkonu.

Pokud mluvíme o debetování prostředků v globální ^osobě, pak je pro ni přijatelná pouze úroveň izolace SERIALIZE, protože peníze musí být utraceny přísně sekvenčně, jinak je možné utratit stejnou částku několikrát.

4. Trvanlivost

Provedl jsem testy s tvrdým řezáním nádoby pomocí

docker kill my-iris

Báze je snášela dobře. Nebyly zjištěny žádné problémy.

Závěr

Pro globální uživatele má InterSystems IRIS podporu transakcí. Jsou skutečně atomové a spolehlivé. K zajištění konzistence databáze založené na globálech je zapotřebí úsilí programátora a použití transakcí, protože nemá složité vestavěné konstrukce, jako jsou cizí klíče.

Úroveň izolace globalů bez použití zámků je READ UNCOMMITED a při použití zámků může být zajištěna až do úrovně SERIALIZE.

Správnost a rychlost transakcí na globalech velmi závisí na dovednosti programátora: čím více sdílených zámků se při čtení používá, tím vyšší je úroveň izolace a čím úžeji se berou exkluzivní zámky, tím rychlejší je výkon.

Zdroj: www.habr.com

Přidat komentář