Mga Transaksyon sa InterSystems IRIS globals

Mga Transaksyon sa InterSystems IRIS globalsAng InterSystems IRIS DBMS ay sumusuporta sa mga kawili-wiling istruktura para sa pag-iimbak ng data - mga global. Sa pangkalahatan, ang mga ito ay mga multi-level na key na may iba't ibang karagdagang goodies sa anyo ng mga transaksyon, mabilis na pag-andar para sa pagtawid sa mga puno ng data, mga kandado at sarili nitong wika ng ObjectScript.

Magbasa nang higit pa tungkol sa mga pandaigdigan sa serye ng mga artikulong "Ang mga pandaigdigan ay mga treasure-swords para sa pag-iimbak ng data":

Mga puno. Bahagi 1
Mga puno. Bahagi 2
Kalat-kalat na mga array. Bahagi 3

Naging interesado ako sa kung paano ipinapatupad ang mga transaksyon sa mga global, kung anong mga feature ang mayroon. Pagkatapos ng lahat, ito ay isang ganap na naiibang istraktura para sa pag-iimbak ng data kaysa sa karaniwang mga talahanayan. Mas mababang antas.

Tulad ng nalalaman mula sa teorya ng relational database, ang isang mahusay na pagpapatupad ng mga transaksyon ay dapat matugunan ang mga kinakailangan ACID:

A - Atomic (atomicity). Ang lahat ng mga pagbabagong ginawa sa transaksyon o wala sa lahat ay naitala.

C - Consistency. Pagkatapos makumpleto ang isang transaksyon, ang lohikal na estado ng database ay dapat na panloob na pare-pareho. Sa maraming paraan ang pangangailangang ito ay may kinalaman sa programmer, ngunit sa kaso ng mga database ng SQL ito ay may kinalaman din sa mga dayuhang susi.

Ako - Ihiwalay. Ang mga transaksyong tumatakbo nang magkatulad ay hindi dapat makaapekto sa isa't isa.

D - Matibay. Pagkatapos ng matagumpay na pagkumpleto ng isang transaksyon, ang mga problema sa mas mababang antas (halimbawa, power failure) ay hindi dapat makaapekto sa data na binago ng transaksyon.

Ang mga pandaigdigan ay mga istruktura ng data na hindi nauugnay. Idinisenyo ang mga ito upang tumakbo nang napakabilis sa limitadong hardware. Tingnan natin ang pagpapatupad ng mga transaksyon sa globals gamit opisyal na imahe ng IRIS docker.

Upang suportahan ang mga transaksyon sa IRIS, ang mga sumusunod na command ay ginagamit: TSTART, TCOMMIT, TROLLBACK.

1. Atomicity

Ang pinakamadaling paraan upang suriin ay atomicity. Sinusuri namin mula sa database console.

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

Pagkatapos ay tapusin namin:

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

Nakukuha namin:

1 2 3

Maayos ang lahat. Ang atomicity ay pinananatili: lahat ng mga pagbabago ay naitala.

Gawin natin ang gawain, magpakilala ng isang error at tingnan kung paano nai-save ang transaksyon, bahagyang o hindi man.

Suriin natin muli ang atomicity:

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

Pagkatapos ay pilit naming ihihinto ang lalagyan, ilulunsad ito at tingnan.

docker kill my-iris

Ang command na ito ay halos katumbas ng force shutdown, dahil nagpapadala ito ng signal ng SIGKILL upang ihinto kaagad ang proseso.

Marahil ay bahagyang na-save ang transaksyon?

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

- Hindi, hindi ito nakaligtas.

Subukan natin ang rollback command:

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)

Wala ring nakaligtas.

2. Consistency

Dahil sa mga database na nakabatay sa mga global, ang mga susi ay ginagawa din sa mga global (paalalahanan ko kayo na ang isang global ay isang mas mababang antas ng istraktura para sa pag-iimbak ng data kaysa sa isang talahanayan ng pamanggit), upang matugunan ang kinakailangan sa pagkakapare-pareho, ang isang pagbabago sa susi ay dapat isama sa parehong transaksyon bilang isang pagbabago sa global.

Halimbawa, mayroon tayong pandaigdigang ^tao, kung saan nag-iimbak tayo ng mga personalidad at ginagamit natin ang TIN bilang susi.

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

Upang magkaroon ng mabilis na paghahanap sa pamamagitan ng apelyido at pangalan, ginawa namin ang ^index key.

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

Upang maging pare-pareho ang database, dapat nating idagdag ang persona tulad nito:

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

Alinsunod dito, kapag nagde-delete kailangan din naming gumamit ng transaksyon:

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

Sa madaling salita, ang pagtupad sa kinakailangan sa pagkakapare-pareho ay ganap na nakasalalay sa mga balikat ng programmer. Ngunit pagdating sa globals, ito ay normal, dahil sa kanilang mababang antas ng kalikasan.

3. Paghihiwalay

Dito nagsisimula ang mga ligaw. Maraming mga gumagamit ang sabay-sabay na nagtatrabaho sa parehong database, binabago ang parehong data.

Ang sitwasyon ay maihahambing sa kapag maraming mga user ang sabay-sabay na gumagana sa parehong code repository at subukang sabay-sabay na gumawa ng mga pagbabago sa maraming mga file nang sabay-sabay.

Dapat ayusin ng database ang lahat sa real time. Isinasaalang-alang na sa mga seryosong kumpanya mayroong kahit isang espesyal na tao na responsable para sa kontrol ng bersyon (para sa pagsasama-sama ng mga sangay, paglutas ng mga salungatan, atbp.), At dapat gawin ng database ang lahat ng ito sa real time, ang pagiging kumplikado ng gawain at ang kawastuhan ng disenyo ng database at code na nagsisilbi dito.

Hindi maintindihan ng database ang kahulugan ng mga pagkilos na ginawa ng mga user upang maiwasan ang mga salungatan kung nagtatrabaho sila sa parehong data. Maaari lamang nitong i-undo ang isang transaksyon na sumasalungat sa isa pa, o isagawa ang mga ito nang sunud-sunod.

Ang isa pang problema ay na sa panahon ng pagpapatupad ng isang transaksyon (bago ang isang commit), ang estado ng database ay maaaring hindi pare-pareho, kaya ito ay kanais-nais na ang iba pang mga transaksyon ay walang access sa hindi pare-pareho ang estado ng database, na kung saan ay nakamit sa relational database. sa maraming paraan: paggawa ng mga snapshot, multi-versioning row at iba pa.

Kapag nagsasagawa ng mga transaksyon nang magkatulad, mahalaga para sa amin na hindi sila makagambala sa isa't isa. Ito ang pag-aari ng paghihiwalay.

Tinutukoy ng SQL ang 4 na antas ng paghihiwalay:

  • BASAHIN NG WALANG KOMIT
  • BASAHIN NG KOMITTO
  • PAULIT-ULIT NA BASAHIN
  • SERIALIZABLE

Tingnan natin ang bawat antas nang hiwalay. Ang mga gastos sa pagpapatupad ng bawat antas ay lumalaki halos exponentially.

BASAHIN NG WALANG KOMIT - ito ang pinakamababang antas ng paghihiwalay, ngunit sa parehong oras ang pinakamabilis. Maaaring basahin ng mga transaksyon ang mga pagbabagong ginawa ng bawat isa.

BASAHIN NG KOMITTO ay ang susunod na antas ng paghihiwalay, na isang kompromiso. Hindi mabasa ng mga transaksyon ang mga pagbabago ng bawat isa bago ang commit, ngunit mababasa nila ang anumang mga pagbabagong ginawa pagkatapos ng commit.

Kung mayroon kaming mahabang transaksyon na T1, kung saan naganap ang mga commit sa mga transaksyong T2, T3 ... Tn, na nagtrabaho sa parehong data tulad ng T1, pagkatapos kapag humihiling ng data sa T1 makakakuha kami ng ibang resulta sa bawat oras. Ang hindi pangkaraniwang bagay na ito ay tinatawag na hindi nauulit na pagbasa.

PAULIT-ULIT NA BASAHIN — sa antas ng paghihiwalay na ito ay wala kaming phenomenon ng hindi nauulit na pagbabasa, dahil sa katotohanan na para sa bawat kahilingang magbasa ng data, isang snapshot ng data ng resulta ang nalilikha at kapag ginamit muli sa parehong transaksyon, ang data mula sa snapshot Ginagamit. Gayunpaman, posibleng basahin ang phantom data sa antas ng paghihiwalay na ito. Tumutukoy ito sa pagbabasa ng mga bagong row na idinagdag ng mga parallel committed na transaksyon.

SERIALIZABLE - ang pinakamataas na antas ng pagkakabukod. Ito ay nailalarawan sa pamamagitan ng katotohanan na ang data na ginamit sa anumang paraan sa isang transaksyon (pagbabasa o pagbabago) ay magagamit sa iba pang mga transaksyon pagkatapos lamang makumpleto ang unang transaksyon.

Una, alamin natin kung mayroong paghihiwalay ng mga operasyon sa isang transaksyon mula sa pangunahing thread. Buksan natin ang 2 terminal window.

Kill ^t

Write ^t(1)
2

TSTART
Set ^t(1)=2

Walang paghihiwalay. Nakikita ng isang thread kung ano ang ginagawa ng pangalawa na nagbukas ng transaksyon.

Tingnan natin kung nakikita ng mga transaksyon ng iba't ibang thread kung ano ang nangyayari sa loob ng mga ito.

Magbukas tayo ng 2 terminal window at magbukas ng 2 transaksyon nang magkatulad.

kill ^t
TSTART
Write ^t(1)
3

TSTART
Set ^t(1)=3

Nakikita ng mga parallel na transaksyon ang data ng bawat isa. Kaya, nakuha namin ang pinakasimpleng, ngunit din ang pinakamabilis na antas ng paghihiwalay, READ UNCOMMITED.

Sa prinsipyo, ito ay maaaring asahan para sa mga global, kung saan ang pagganap ay palaging priyoridad.

Paano kung kailangan natin ng mas mataas na antas ng paghihiwalay sa mga operasyon sa mga global?

Dito kailangan mong isipin kung bakit kailangan ang mga antas ng paghihiwalay at kung paano gumagana ang mga ito.

Ang pinakamataas na antas ng paghihiwalay, ang SERIALIZE, ay nangangahulugan na ang resulta ng mga transaksyon na isinagawa nang magkatulad ay katumbas ng kanilang sunud-sunod na pagpapatupad, na ginagarantiyahan ang kawalan ng mga banggaan.

Magagawa natin ito gamit ang mga smart lock sa ObjectScript, na maraming iba't ibang gamit: maaari mong gawin ang regular, incremental, multiple locking gamit ang command. Ikandado.

Ang mas mababang antas ng paghihiwalay ay mga trade-off na idinisenyo upang pataasin ang bilis ng database.

Tingnan natin kung paano natin makakamit ang iba't ibang antas ng paghihiwalay gamit ang mga kandado.

Binibigyang-daan ka ng operator na ito na kumuha hindi lamang ng mga eksklusibong kandado na kailangan upang baguhin ang data, ngunit ang tinatawag na mga shared lock, na maaaring tumagal ng ilang mga thread nang magkatulad kapag kailangan nilang magbasa ng data na hindi dapat baguhin ng ibang mga proseso sa panahon ng proseso ng pagbabasa.

Higit pang impormasyon tungkol sa two-phase blocking method sa Russian at English:

Dalawang-phase na pagharang
Dalawang-phase na pag-lock

Ang kahirapan ay na sa panahon ng isang transaksyon ang estado ng database ay maaaring hindi pare-pareho, ngunit ang hindi tugmang data na ito ay nakikita ng iba pang mga proseso. Paano ito maiiwasan?

Gamit ang mga lock, gagawa kami ng visibility window kung saan magiging pare-pareho ang estado ng database. At lahat ng pag-access sa naturang mga bintana ng visibility ng napagkasunduang estado ay makokontrol ng mga kandado.

Ang mga nakabahaging lock sa parehong data ay magagamit muli—maaaring tumagal ang mga ito ng ilang proseso. Pinipigilan ng mga lock na ito ang iba pang mga proseso sa pagbabago ng data, i.e. ginagamit ang mga ito upang bumuo ng mga bintana ng pare-parehong estado ng database.

Ginagamit ang mga eksklusibong lock para sa mga pagbabago ng data - isang proseso lang ang maaaring tumagal ng ganoong lock. Ang isang eksklusibong lock ay maaaring kunin sa pamamagitan ng:

  1. Anumang proseso kung ang data ay libre
  2. Ang proseso lang na may nakabahaging lock sa data na ito at ang unang humiling ng eksklusibong lock.

Mga Transaksyon sa InterSystems IRIS globals

Ang mas makitid na window ng visibility, mas matagal ang iba pang mga proseso na kailangang maghintay para dito, ngunit mas pare-pareho ang estado ng database sa loob nito.

READ_COMMITTED — ang kakanyahan ng antas na ito ay nakikita lamang natin ang nakatuong data mula sa iba pang mga thread. Kung ang data sa isa pang transaksyon ay hindi pa nagagawa, makikita natin ang lumang bersyon nito.

Nagbibigay-daan ito sa amin na iparallelize ang trabaho sa halip na hintayin na mailabas ang lock.

Kung walang mga espesyal na trick, hindi namin makikita ang lumang bersyon ng data sa IRIS, kaya kailangan naming gawin ang mga lock.

Alinsunod dito, kakailanganin naming gumamit ng mga nakabahaging lock upang payagan ang data na basahin lamang sa mga sandali ng pagkakapare-pareho.

Sabihin nating mayroon tayong user base ^tao na naglilipat ng pera sa isa't isa.

Sandali ng paglipat mula sa taong 123 patungo sa taong 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)

Ang sandali ng paghiling ng halaga ng pera mula sa taong 123 bago ang pag-debit ay dapat na may kasamang eksklusibong bloke (bilang default):

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

At kung kailangan mong ipakita ang katayuan ng account sa iyong personal na account, maaari mong gamitin ang isang nakabahaging lock o hindi mo ito gamitin:

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

Gayunpaman, kung ipagpalagay natin na ang mga pagpapatakbo ng database ay ginaganap halos kaagad (paalalahanan kita na ang mga global ay isang mas mababang antas ng istraktura kaysa sa isang relational na talahanayan), kung gayon ang pangangailangan para sa antas na ito ay bumababa.

PAULIT-ULIT NA BASAHIN - Ang antas ng paghihiwalay na ito ay nagbibigay-daan para sa maramihang pagbabasa ng data na maaaring mabago ng mga kasabay na transaksyon.

Alinsunod dito, kakailanganin naming maglagay ng nakabahaging lock sa pagbabasa ng data na binago namin at mga eksklusibong lock sa data na binago namin.

Sa kabutihang palad, pinapayagan ka ng LOCK operator na ilista nang detalyado ang lahat ng kinakailangang mga kandado, kung saan maaaring magkaroon ng marami, sa isang pahayag.

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

iba pang mga operasyon (sa oras na ito, sinusubukan ng mga parallel thread na baguhin ang ^tao(123, halaga), ngunit hindi maaaring)

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

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

Kapag naglilista ng mga kandado na pinaghihiwalay ng mga kuwit, kinukuha ang mga ito nang sunud-sunod, ngunit kung gagawin mo ito:

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

pagkatapos sila ay kinuha nang sabay-sabay nang atomiko.

SERYADO — Kakailanganin nating magtakda ng mga kandado upang sa huli ang lahat ng mga transaksyon na may karaniwang data ay isinasagawa nang sunud-sunod. Para sa diskarteng ito, ang karamihan sa mga lock ay dapat na eksklusibo at kinuha sa pinakamaliit na bahagi ng pandaigdigan para sa pagganap.

Kung pag-uusapan natin ang tungkol sa pag-debit ng mga pondo sa pandaigdigang ^tao, kung gayon ang antas ng paghihiwalay ng SERIALIZE lamang ang katanggap-tanggap para dito, dahil ang pera ay dapat na mahigpit na gastusin nang sunud-sunod, kung hindi, posibleng gumastos ng parehong halaga nang maraming beses.

4. tibay

Nagsagawa ako ng mga pagsubok na may matigas na pagputol ng lalagyan gamit

docker kill my-iris

Pinahintulutan sila ng base. Walang natukoy na mga problema.

Konklusyon

Para sa mga global, ang InterSystems IRIS ay may suporta sa transaksyon. Ang mga ito ay tunay na atomic at maaasahan. Upang matiyak ang pagkakapare-pareho ng isang database batay sa mga global, ang mga pagsisikap ng programmer at ang paggamit ng mga transaksyon ay kinakailangan, dahil wala itong mga kumplikadong built-in na konstruksyon tulad ng mga foreign key.

Ang antas ng paghihiwalay ng mga global na hindi gumagamit ng mga kandado ay READ UNCOMMITED, at kapag gumagamit ng mga kandado maaari itong matiyak hanggang sa antas ng SERIALIZE.

Ang kawastuhan at bilis ng mga transaksyon sa mga global ay lubos na nakasalalay sa kakayahan ng programmer: ang mas malawak na nakabahaging mga kandado ay ginagamit kapag nagbabasa, mas mataas ang antas ng paghihiwalay, at mas makitid na eksklusibong mga kandado ang kinukuha, mas mabilis ang pagganap.

Pinagmulan: www.habr.com

Magdagdag ng komento