Transaksjonar i globalar InterSystems IRIS

Transaksjonar i globalar InterSystems IRISDBMS InterSystems IRIS stĂžttar interessante strukturar for datalagring — globalar. I utgangspunktet er dette flernivĂ„-nĂžkklar med ulike ekstra funksjonar som transaksjonar, raske funksjonar for Ă„ navigere i datatrĂŠr, lĂ„sing, og sitt eige sprĂ„k, ObjectScript.

Les meir om globalar i artikkelen «Globalar — sverd og skjold for datalagring»:

TrĂŠr. Del 1
TrĂŠr. Del 2
Sparsomme matriser. Del 3

Eg vart nysgjerrig pÄ korleis transaksjonar er implementert i globalar, og kva som gjer desse strukturane unike. Det er ein heilt annan mÄte Ä lagre data pÄ enn dei vante tabellane. Mye meir lÄg-nivÄ.

Som kjent frÄ teorien om relasjonsdatabasar, bÞr ein god implementering av transaksjonar oppfylle krava ACID:

A — Atomic (atomaritet). Alle endringar som er gjort i transaksjonen skal registrerast, eller inga endring skal gjerast.

C — Consistency (konsistens). Etter at transaksjonen er fullfĂžrt, mĂ„ den logiske tilstanden til databasen vere internt konsistent. Dette kravet angĂ„r i stor grad programmaren, men i tilfelle av SQL-databasar, er det ogsĂ„ knytt til utanlandske nĂžklar.

I — Isolate (isolasjon). Parallelt utfĂžrte transaksjonar skal ikkje pĂ„verke kvarandre.

D — Durable (varigheit). Etter vellykka avslutning av transaksjonen, skal problem pĂ„ lavare nivĂ„ (for eksempel strĂžmsvikt) ikkje pĂ„verke data som er endra av transaksjonen.

Globalar er ikkje-relasjonelle datastrukturar. Dei vart utvikla for ekstrem rask drifting pÄ svÊrt avgrensa maskinvare. La oss sjÄ nÊrare pÄ implementasjonen av transaksjonar i globalar ved hjelp av den offisielle docker-bilete av IRIS.

For Ă„ stĂžtte transaksjonar i IRIS, blir fĂžlgjande kommandoar brukte: TSTART, TCOMMIT, TROLLBACK.

1. Atomaritet

Det er lettast Ä sjekke atomaritet. Vi sjekkar frÄ databasen sin konsoll.

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

SÄ fÄr vi ut:

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

Vi fÄr:

1 2 3

Alt i orden. Atomaritet er oppretthaldt: alle endringar er registrert.

La oss gjere oppgÄva meir komplisert, vi introduserer ein feil og sjekkar korleis transaksjonen blir bevart, delvis eller ikkje i det heile.

La oss sjekke atomariteten igjen:

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

Deretter tvingar vi til Ă„ stoppe containeren, startar opp igjen og ser kva som skjer.

docker kill my-iris

Denne kommandoen er praktisk talt lik en tvungen strĂžmavstengning, da den sender et umiddelbart stoppesignal til prosessen SIGKILL.

Kan det ha vĂŠrt at transaksjonen ble delvis lagret?

WRITE ^a(1), ^a(2), ^a(3)
^
 ^a(1)

— Nei, den ble ikke lagret.

La oss prĂžve tilbakefĂžringskommandoen:

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

WRITE ^a(1), ^a(2), ^a(3)
^
 ^a(1)

Det ble heller ikke lagret noe.

2. Konsistens

Siden i globale databaser blir nÞklene ogsÄ laget pÄ globale nivÄer (la meg minne om at global er en lavere nivÄstruktur for datalagring enn en relasjonell tabell), for Ä oppfylle kravene til konsistens mÄ endringen av nÞkkelen inkluderes i den samme transaksjonen som endringen av globalen.

For eksempel har vi en global ^person, der vi lagrer personlige data og bruker identitetsnummeret som nĂžkkel.

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

For Ă„ ha rask sĂžking etter etternavn og fornavn laget vi nĂžkkelen ^index.

^index('Kamenev', 'Sergey', 1234567) = 1

For at databasen skal vÊre konsistent, mÄ vi legge til personen slik:

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

Dermed mÄ vi ogsÄ bruke en transaksjon ved sletting:

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

Med andre ord ligger oppfyllelsen av konsistenskravene helt pÄ utviklerens skuldre. Men nÄr det gjelder globale, er dette normalt, pÄ grunn av deres lavnivÄnatur.

3. Isolasjon

Her begynner det Ă„ bli komplisert. Mange brukere arbeider samtidig med den samme databasen, endrer de samme dataene.

Situasjonen kan sammenlignes med at mange brukere samtidig jobber med det samme kode-repositoriet og prÞver Ä commite endringer i mange filer pÄ en gang.

Databasen mÄ hÄndtere alt dette i sanntid. Gitt at i seriÞse selskaper finnes det til og med en spesialperson som er ansvarlig for versjonskontroll (for Ä slÄ sammen grener, lÞse konflikter, osv.), og at databasen mÄ gjÞre alt dette i sanntid, blir oppgavens kompleksitet og korrektheten i databasedesign og koden som vedlikeholder den Äpenbar.

Databasen kan ikke forstÄ meningen med de handlingene som brukerne utfÞrer for Ä forhindre konflikter nÄr de jobber med de samme dataene. Den kan bare tilbakekalle en transaksjon som strider mot en annen eller utfÞre dem sekvensielt.

Et annet problem er at under utfÞrelsen av en transaksjon (fÞr kommittering) kan databasen vÊre i en inkonsekvent tilstand, derfor er det Þnskelig at andre transaksjoner ikke har tilgang til den inkonsekvente tilstanden til databasen, noe som oppnÄs i relasjonsdatabaser pÄ mange mÄter: ved Ä lage Þyeblikksbilder, ved fleredelelinje og sÄ videre.

NÄr transaksjoner kjÞres parallelt, er det viktig at de ikke forstyrrer hverandre. Dette er egenskapen av isolasjon.

SQL definerer 4 nivÄer av isolasjon:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

La oss se pÄ hvert nivÄ separat. Kostnadene for implementering av hvert nivÄ Þker nesten eksponentielt.

READ UNCOMMITTED — dette er det laveste isolasjonsnivĂ„et, men samtidig det raskeste. Transaksjoner kan lese endringer gjort av hverandre.

READ COMMITTED — dette er det neste isolasjonsnivĂ„et, som er et kompromiss. Transaksjoner kan ikke lese endringer gjort av hverandre fĂžr kommittering, men kan lese eventuelle endringer gjort etter kommittering.

Hvis vi har en lang transaksjon T1, der det har vért kommitteringer i transaksjonene T2, T3
 Tn, som har arbeidet med de samme dataene som T1, vil vi hver gang vi forespþr data i T1 motta ulike resultater. Dette fenomenet kalles ikke-repetert lesing.

REPEATABLE READ — pĂ„ dette isolasjonsnivĂ„et har vi ikke fenomenet ikke-repetert lesing, fordi for hver forespĂžrsel om Ă„ lese data opprettes et Ăžyeblikksbilde av dataresultatet, og nĂ„r det brukes igjen i denne samme transaksjonen, benyttes dataene fra Ăžyeblikksbildet. Imidlertid er det pĂ„ dette isolasjonsnivĂ„et mulig Ă„ lese fantomdata. Dette gjelder lesing av nye rader som er lagt til av parallelle bekreftede transaksjoner.

SERIALIZABLE — det hĂžyeste isolasjonsnivĂ„et. Det er preget av det at data som pĂ„ en eller annen mĂ„te brukes i transaksjonen (lesing eller endring) blir tilgjengelige for andre transaksjoner fĂžrst etter at den fĂžrste transaksjonen er fullfĂžrt.

FÞrst mÄ vi se pÄ om det er isolasjon av operasjonene i transaksjonen fra hovedstrÞmmen. Vi Äpner 2 terminalvinduer.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

Det finnes ingen isolasjon. Én strĂžm ser hva den andre som har Ă„pnet transaksjonen gjĂžr.

La oss se om transaksjoner fra forskjellige strĂžmmer ser hva som skjer inni dem.

Vi Äpner 2 terminalvinduer og Äpner 2 transaksjoner parallelt.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Parallele transaksjoner ser dataene til hverandre. SÄ vi har fÄtt det enkleste, men ogsÄ raskeste nivÄet av isolasjon, READ UNCOMMITTED.

Dette var i prinsippet Ă„ forvente for globale variabler, hvor ytelse alltid har vĂŠrt i fokus.

Hva om vi trenger et hÞyere nivÄ av isolasjon for operasjoner pÄ globale variabler?

Her mÄ vi tenke pÄ hvorfor nivÄer av isolasjon er nÞdvendige og hvordan de fungerer.

Det hÞyeste nivÄet av isolasjon, SERIALIZE, betyr at resultatet av parallelt utfÞrte transaksjoner er ekvivalent med sekvensiell utfÞring, som garanterer fravÊr av kollisjoner.

Dette kan vi oppnÄ med hensiktsmessige lÄser i ObjectScript, som har mange forskjellige bruksomrÄder: man kan gjÞre vanlig, inkrementell, eller flere lÄsinger med kommandoen LOCK.

Lavere nivÄer av isolasjon er kompromisser laget for Ä Þke databasehastigheten.

La oss se hvordan vi kan oppnÄ ulike nivÄer av isolasjon med hjelp av lÄser.

Denne operatoren gjÞr det mulig Ä ta ikke bare eksklusive lÄser, som er nÞdvendige for Ä endre data, men ogsÄ sÄkalte delte lÄser, som flere strÞmmer kan ta parallelt nÄr de trenger Ä lese data som ikke skal endres av andre prosesser under lesing.

Mer om tofaselÄsingsmetoden pÄ russisk og engelsk:

→ TofaselĂ„sning
→ Two-phase locking

Vanskeligheten er at under transaksjonen kan databasens tilstand vÊre inkonsistent, men disse inkonsistente dataene er synlige for andre prosesser. Hvordan unngÄ dette?

Vi vil lage synsvinduer med lÄser der databasens tilstand er konsistent. Og all tilgang til slike vinduer med konsistent tilstand vil bli kontrollert av lÄser.

Delte blokkeringer av de samme dataene er gjenbrukbare — flere prosesser kan ta dem. Disse blokkeringene forhindrer andre prosesser i Ă„ endre dataene, dvs. de brukes til Ă„ danne vinduer av konsistent tilstand i databasen.

Eksklusive blokkeringer brukes for Ă„ endre data — en slik blokkering kan kun tas av Ă©n prosess. En eksklusiv blokkering kan tas av:

  1. Enhver prosess, hvis dataene er frie.
  2. Bare den prosessen som har en delt blokkering pÄ disse dataene og fÞrst har bedt om en eksklusiv blokkering.

Transaksjonar i globalar InterSystems IRIS

Jo smalere synsvinduet er, desto lengre mÄ andre prosesser vente, men desto mer konsistent kan tilstanden i databasen vÊre i det.

READ_COMMITED — essensen av dette nivĂ„et er at vi bare ser bekreftede data fra andre strĂžmmer. Hvis dataene i en annen transaksjon ikke er bekreftet ennĂ„, ser vi den gamle versjonen.

Dette lar oss parallellisere arbeidet i stedet for Ä vente pÄ at blokkeringen skal frigjÞres.

Uten spesielle triks vil vi ikke kunne se den gamle versjonen av dataene i IRIS, sÄ vi mÄ nÞye oss med blokkeringer.

Tilsvarende mÄ vi med hjelp av delte blokkeringer tillate lesing av data bare i konsistensÞyeblikkene.

La oss si at vi har en brukerbase ^person som overfĂžrer penger til hverandre.

Tidspunktet for overfĂžring fra person 123 til person 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)

Tidspunktet for forespÞrselen om hvor mye penger person 123 har fÞr fradrag, mÄ vÊre ledsaget av en eksklusiv blokkering (som standard):

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

Men hvis man trenger Ă„ vise kontobalansen i den personlige kontoen, kan man bruke en delt blokkering eller helt la vĂŠre Ă„ bruke den:

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

Imidlertid, hvis vi antar at operasjoner med databasen utfÞres praktisk talt Þyeblikkelig (jeg minner om at globale variabler er en langt mer lavnivÄstruktur enn relasjonsdatabasen), faller behovet for dette nivÄet.

REPEATABLE READ — pĂ„ dette isolasjonsnivĂ„et er det tillatt at det kan vĂŠre flere lesinger av data som kan endres av parallelle transaksjoner.

Tilsvarende mÄ vi sette en delt blokkering pÄ dataene vi endrer og eksklusive blokkeringer pÄ dataene vi endrer.

Operatoren LOCK gjÞr det mulig Ä detaljere alle nÞdvendige lÄser i én enkelt operator, som kan vÊre mange.

LOCK +^person(123, amount)#”S”
lesing av ^person(123, amount)

andre operasjoner (mens parallelle strĂžmmer prĂžver Ă„ endre ^person(123, amount), men ikke kan)

LOCK +^person(123, amount)
endre ^person(123, amount)
LOCK -^person(123, amount)

lesing av ^person(123, amount)
LOCK -^person(123, amount)#”S”

Ved Ä liste opp lÄser med komma tas de sekvensielt, men hvis vi gjÞr det slik:

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

tas de atomĂŠrt alle samtidig.

SERIALIZE — vi mĂ„ sette opp lĂ„ser slik at til slutt alle transaksjoner som har felles data utfĂžres sekvensielt. For denne tilnĂŠrmingen mĂ„ de fleste lĂ„ser vĂŠre eksklusive og tas pĂ„ de minste omrĂ„dene av globalen for ytelse.

NÄr det gjelder uttak av midler i globalen ^person, er det kun SERIALIZE-isolasjonsnivÄet som er akseptabelt, siden penger mÄ brukes strengt sekvensielt, ellers kan man risikere Ä bruke den samme summen flere ganger.

4. Varighet

Jeg har utfĂžrt tester med hard nedstengning av containeren ved hjelp av

docker kill my-iris

Databasen hÄndterte dette godt. Det ble ikke oppdaget noen problemer.

Konklusjon

For globaler i InterSystems IRIS er det stÞtte for transaksjoner. De er faktisk atomÊre, pÄlitelige. For Ä sikre konsistensen i databasen pÄ globaler er det nÞdvendig med programmererens innsats og bruk av transaksjoner, da det ikke er komplekse innebygde konstruksjoner som fremmednÞkler.

IsolasjonsnivÄet for globaler uten bruk av lÄser er READ UNCOMMITED, og ved bruk av lÄser kan det sikres opp til SERIALIZE-nivÄet.

Korrekthet og hastighet pÄ transaksjoner i globaler avhenger sterkt av programmererens dyktighet: jo mer omfattende shared-lÄser brukes under lesing, desto hÞyere er isolasjonsnivÄet, og jo smalere eksklusive lÄser brukes, desto bedre ytelse.

Kilde: habr.com

KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere đŸ”„ KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster