Transaksies in InterSystems IRIS globals

Transaksies in InterSystems IRIS globalsDie InterSystems IRIS DBMS ondersteun interessante strukture vir die stoor van data - globale. In wese is dit multi-vlak sleutels met verskeie bykomende goedjies in die vorm van transaksies, vinnige funksies om databome te deurkruis, slotte en sy eie ObjectScript-taal.

Lees meer oor globals in die reeks artikels “Globals is skat-swaarde vir die stoor van data”:

Bome. Deel 1
Bome. Deel 2
Skaars skikkings. Deel 3

Ek het geïnteresseerd geraak in hoe transaksies in globale geïmplementeer word, watter kenmerke daar is. Dit is immers 'n heeltemal ander struktuur vir die stoor van data as die gewone tabelle. Baie laer vlak.

Soos bekend uit die teorie van relasionele databasisse, moet 'n goeie implementering van transaksies aan die vereistes voldoen ACID:

A - Atoom (atomisiteit). Alle veranderinge wat in die transaksie gemaak is of glad nie, word aangeteken.

C - Konsekwentheid. Nadat 'n transaksie voltooi is, moet die logiese toestand van die databasis intern konsekwent wees. In baie opsigte het hierdie vereiste betrekking op die programmeerder, maar in die geval van SQL-databasisse het dit ook betrekking op vreemde sleutels.

Ek - Isoleer. Transaksies wat parallel loop, behoort nie mekaar te beïnvloed nie.

D - Duursaam. Na suksesvolle voltooiing van 'n transaksie behoort probleme op laer vlakke (kragonderbreking, byvoorbeeld) nie die data wat deur die transaksie verander word, te beïnvloed nie.

Globale is nie-relasionele datastrukture. Hulle is ontwerp om super vinnig te werk op baie beperkte hardeware. Kom ons kyk na die implementering van transaksies in globale met behulp van amptelike IRIS docker beeld.

Om transaksies in IRIS te ondersteun, word die volgende opdragte gebruik: TSTART, TCOMMIT, TERUGTROL.

1. Atomiteit

Die maklikste manier om na te gaan is atomiteit. Ons kyk vanaf die databasiskonsole.

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

Dan sluit ons af:

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

Ons kry:

1 2 3

Alles is reg. Atomisiteit word gehandhaaf: alle veranderinge word aangeteken.

Kom ons bemoeilik die taak, stel 'n fout voor en kyk hoe die transaksie gestoor word, gedeeltelik of glad nie.

Kom ons kyk weer na die atomiteit:

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

Dan sal ons die houer met geweld stop, dit lanseer en kyk.

docker kill my-iris

Hierdie opdrag is amper gelykstaande aan 'n kragafsluiting, aangesien dit 'n SIGKILL-sein stuur om die proses onmiddellik te stop.

Miskien is die transaksie gedeeltelik gered?

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

- Nee, dit het nie oorleef nie.

Kom ons probeer die terugrol-opdrag:

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)

Niks het ook oorleef nie.

2. Konsekwentheid

Aangesien in databasisse wat op globales gebaseer is, sleutels ook op globale gemaak word (laat ek jou daaraan herinner dat 'n globale 'n laervlakstruktuur is vir die stoor van data as 'n relasionele tabel), om aan die konsekwentheidsvereiste te voldoen, moet 'n verandering in die sleutel ingesluit word in dieselfde transaksie as 'n verandering in die globale.

Ons het byvoorbeeld 'n globale ^persoon waarin ons persoonlikhede stoor en ons gebruik die TIN as 'n sleutel.

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

Om 'n vinnige soektog volgens van en voornaam te hê, het ons die ^index-sleutel gemaak.

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

Om die databasis konsekwent te maak, moet ons die persona soos volg byvoeg:

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

Gevolglik, wanneer ons uitvee moet ons ook 'n transaksie gebruik:

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

Met ander woorde, die nakoming van die konsekwentheidsvereiste berus geheel en al op die programmeerder se skouers. Maar as dit by globales kom, is dit normaal, as gevolg van hul lae-vlak aard.

3. Isolasie

Dit is waar die wildernis begin. Baie gebruikers werk gelyktydig op dieselfde databasis en verander dieselfde data.

Die situasie is vergelykbaar met wanneer baie gebruikers gelyktydig met dieselfde kodebewaarplek werk en probeer om gelyktydig veranderinge aan baie lêers tegelyk toe te pas.

Die databasis behoort dit alles in reële tyd uit te sorteer. As in ag geneem word dat daar in ernstige maatskappye selfs 'n spesiale persoon is wat verantwoordelik is vir weergawebeheer (vir die samesmelting van takke, die oplossing van konflikte, ens.), En die databasis moet dit alles in reële tyd doen, die kompleksiteit van die taak en die korrektheid van die databasisontwerp en kode wat dit dien.

Die databasis kan nie die betekenis verstaan ​​van die handelinge wat deur gebruikers uitgevoer word om konflikte te vermy as hulle op dieselfde data werk nie. Dit kan slegs een transaksie wat bots met 'n ander ongedaan maak, of dit opeenvolgend uitvoer.

Nog 'n probleem is dat tydens die uitvoering van 'n transaksie (voor 'n commit), die toestand van die databasis inkonsekwent kan wees, dus is dit wenslik dat ander transaksies nie toegang het tot die inkonsekwente toestand van die databasis nie, wat in relasionele databasisse bereik word. op baie maniere: skep foto's, multi-weergawe rye en ens.

Wanneer transaksies parallel uitgevoer word, is dit vir ons belangrik dat dit nie met mekaar inmeng nie. Dit is die eienskap van isolasie.

SQL definieer 4 isolasievlakke:

  • LEES ONGELAAT
  • LEES VERPLIG
  • HERHAALBARE LEES
  • SERIALISEERBAAR

Kom ons kyk na elke vlak afsonderlik. Die koste van die implementering van elke vlak groei byna eksponensieel.

LEES ONGELAAT - dit is die laagste vlak van isolasie, maar terselfdertyd die vinnigste. Transaksies kan veranderinge wat deur mekaar gemaak is, lees.

LEES VERPLIG is die volgende vlak van isolasie, wat 'n kompromie is. Transaksies kan nie mekaar se veranderinge voor die commit lees nie, maar hulle kan enige veranderinge wat na die commit gemaak is, lees.

As ons 'n lang transaksie T1 het, waartydens commits plaasgevind het in transaksies T2, T3 ... Tn, wat met dieselfde data as T1 gewerk het, dan wanneer ons data in T1 versoek, sal ons elke keer 'n ander resultaat kry. Hierdie verskynsel word nie-herhaalbare lees genoem.

HERHAALBARE LEES - in hierdie isolasievlak het ons nie die verskynsel van nie-herhaalbare lees nie, as gevolg van die feit dat vir elke versoek om data te lees, 'n momentopname van die resultaatdata geskep word en wanneer dit in dieselfde transaksie hergebruik word, die data van die momentopname is gebruik. Dit is egter moontlik om spookdata op hierdie isolasievlak te lees. Dit verwys na die lees van nuwe rye wat bygevoeg is deur parallelle toegewyde transaksies.

SERIALISEERBAAR - die hoogste vlak van isolasie. Dit word gekenmerk deur die feit dat data wat op enige manier in 'n transaksie gebruik word (lees of verander) eers na die voltooiing van die eerste transaksie vir ander transaksies beskikbaar word.

Laat ons eers uitvind of daar isolasie van bedrywighede in 'n transaksie van die hoofdraad is. Kom ons maak 2 terminale vensters oop.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

Daar is geen isolasie nie. Een draad sien wat die tweede een doen wat die transaksie oopgemaak het.

Kom ons kyk of transaksies van verskillende drade sien wat binne hulle gebeur.

Kom ons maak 2 terminale vensters oop en maak 2 transaksies parallel oop.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Parallelle transaksies sien mekaar se data. So, ons het die eenvoudigste, maar ook die vinnigste isolasievlak, LEES ONVERBIND.

In beginsel kan dit verwag word vir globale, waarvoor prestasie nog altyd 'n prioriteit was.

Wat as ons 'n hoër vlak van isolasie nodig het in bedrywighede op globale?

Hier moet jy dink oor hoekom isolasievlakke hoegenaamd nodig is en hoe dit werk.

Die hoogste isolasievlak, SERIALISEER, beteken dat die resultaat van transaksies wat parallel uitgevoer word gelykstaande is aan hul opeenvolgende uitvoering, wat die afwesigheid van botsings waarborg.

Ons kan dit doen deur slim slotte in ObjectScript te gebruik, wat baie verskillende gebruike het: jy kan gereelde, inkrementele, meervoudige sluiting met die opdrag doen LOCK.

Laer isolasievlakke is afwykings wat ontwerp is om databasisspoed te verhoog.

Kom ons kyk hoe ons verskillende vlakke van isolasie kan bereik deur slotte te gebruik.

Hierdie operateur laat jou toe om nie net eksklusiewe slotte te neem wat nodig is om data te verander nie, maar sogenaamde gedeelde slotte, wat verskeie drade in parallel kan neem wanneer hulle data moet lees wat nie deur ander prosesse tydens die leesproses verander moet word nie.

Meer inligting oor die tweefase-blokkeermetode in Russies en Engels:

Twee-fase blokkering
Twee-fase sluiting

Die probleem is dat tydens 'n transaksie die toestand van die databasis dalk inkonsekwent kan wees, maar hierdie inkonsekwente data is sigbaar vir ander prosesse. Hoe om dit te vermy?

Deur slotte te gebruik, sal ons sigbaarheidsvensters skep waarin die toestand van die databasis konsekwent sal wees. En alle toegang tot sulke vensters van sigbaarheid van die ooreengekome staat sal deur slotte beheer word.

Gedeelde slotte op dieselfde data is herbruikbaar—verskeie prosesse kan dit neem. Hierdie slotte verhoed dat ander prosesse data verander, m.a.w. hulle word gebruik om vensters van konsekwente databasisstatus te vorm.

Eksklusiewe slotte word gebruik vir dataveranderinge - net een proses kan so 'n slot neem. 'n Eksklusiewe slot kan geneem word deur:

  1. Enige proses as die data gratis is
  2. Slegs die proses wat 'n gedeelde slot op hierdie data het en die eerste was wat 'n eksklusiewe slot versoek het.

Transaksies in InterSystems IRIS globals

Hoe smaller die sigbaarheidsvenster, hoe langer moet ander prosesse daarvoor wag, maar hoe meer konsekwent kan die toestand van die databasis daarin wees.

READ_COMMITTED - die essensie van hierdie vlak is dat ons slegs toegewyde data van ander drade sien. As die data in 'n ander transaksie nog nie gepleeg is nie, sien ons die ou weergawe daarvan.

Dit stel ons in staat om die werk te paralleliseer in plaas daarvan om te wag dat die slot vrygestel word.

Sonder spesiale truuks sal ons nie die ou weergawe van die data in IRIS kan sien nie, so ons sal met slotte moet klaarkom.

Gevolglik sal ons gedeelde slotte moet gebruik sodat data slegs gelees kan word op oomblikke van konsekwentheid.

Kom ons sê ons het 'n gebruikersbasis ^persoon wat geld na mekaar oordra.

Oomblik van oordrag van persoon 123 na persoon 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)

Die oomblik waarop die bedrag geld van persoon 123 aangevra word voor debitering moet vergesel word van 'n eksklusiewe blokkie (by verstek):

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

En as jy die rekeningstatus in jou persoonlike rekening moet wys, kan jy 'n gedeelde slot gebruik of dit glad nie gebruik nie:

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

As ons egter aanvaar dat databasisbewerkings byna onmiddellik uitgevoer word (laat ek jou daaraan herinner dat globale struktuur 'n baie laer vlak struktuur as 'n relasionele tabel is), dan verminder die behoefte aan hierdie vlak.

HERHAALBARE LEES - Hierdie isolasievlak maak voorsiening vir veelvuldige lees van data wat deur gelyktydige transaksies gewysig kan word.

Gevolglik sal ons 'n gedeelde slot moet plaas op die lees van die data wat ons verander en eksklusiewe slotte op die data wat ons verander.

Gelukkig laat die LOCK-operateur jou toe om al die nodige slotte, waarvan daar baie kan wees, in een stelling in detail te lys.

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

ander bewerkings (op hierdie tydstip probeer parallelle drade om ^persoon(123, bedrag) te verander, maar kan nie)

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

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

As jy slotte geskei deur kommas lys, word hulle opeenvolgend geneem, maar as jy dit doen:

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

dan word hulle op een slag atomies geneem.

afleveringen — ons sal slotte moet stel sodat uiteindelik alle transaksies wat gemeenskaplike data het, opeenvolgend uitgevoer word. Vir hierdie benadering moet die meeste slotte eksklusief wees en op die kleinste gebiede van die wêreld geneem word vir prestasie.

As ons praat oor die debitering van fondse in die globale ^persoon, dan is slegs die SERIALISEER-isolasievlak daarvoor aanvaarbaar, aangesien geld streng opeenvolgend bestee moet word, anders is dit moontlik om dieselfde bedrag verskeie kere te spandeer.

4. Duursaamheid

Ek het toetse uitgevoer met harde sny van die houer met behulp van

docker kill my-iris

Die basis het hulle goed verdra. Geen probleme is geïdentifiseer nie.

Gevolgtrekking

Vir globale, InterSystems IRIS het transaksie-ondersteuning. Hulle is werklik atoom en betroubaar. Om konsekwentheid van 'n databasis gebaseer op globales te verseker, word programmeerderpogings en die gebruik van transaksies vereis, aangesien dit nie komplekse ingeboude konstrukte soos vreemde sleutels het nie.

Die isolasievlak van globals sonder die gebruik van slotte is LEES ONVERBIND, en wanneer slotte gebruik word, kan dit verseker word tot op die SERIALISEER-vlak.

Die korrektheid en spoed van transaksies op globales hang baie af van die vaardigheid van die programmeerder: hoe meer gedeelde slotte gebruik word tydens lees, hoe hoër is die vlak van isolasie, en hoe meer eksklusiewe slotte geneem word, hoe vinniger is die werkverrigting.

Bron: will.com

Voeg 'n opmerking