R & G Chapter 17 ~ samanaikaisuus Tietokannalla on tyypillisesti useita samanaikaisia käyttäjiä (prosesseja). On toivottavaa, että: Yhdenkään käyttäjän toiminta ei hidastuisi kohtuuttomasti, vaikka muita käyttäjiä olisi runsaastikin. Yhdenkään käyttäjän toiminta ei ainakaan saisi estyä kokonaan Tietokanta säilyy eheänä eli, että jokaisen transaktion toiminnot pysyvät periaatteessa loogisesti erillään muiden transaktioiden toiminnoista (eristyvyys; välttämätön ominaisuus). 1 2 (transaction management) on keskeinen tekijä tietokannan samanaikaisen käytön ja virheistä toipumisen kannalta. Useat prosessit voivat käsitellä tietokantaa samanaikaisesti Jos tietokoneessa on vain yksi prosessori, samanaikaiset prosessit toimivat limittäin (interleaved) ja jakavat prosessorikapasiteettia. Jos koneessa on monta prosessoria, prosessit voivat toimia aidosti yhtä aikaa eri prosessoreilla (parallel). Jatkossa tarkastellaan limittäisyyteen perustuvaa samanaikaisuutta. Tietokantatransaktio on tietokantaa käsittelevä prosessin osa, jonka vaikutusten halutaan muodostavan yhden jakamattoman (atomisen) kokonaisuuden. voi sisältää hakuja, lisäyksiä, poistoja muutoksia Esim. pankkitilisovelluksen proseduuri tilisiirto(t1, t2, x), joka siirtää x mk tililtä t1 tilille t2: begin transaction update tili set saldo=saldo-x where tilinro=t1; update tili set saldo=saldo+x where tilinro=t2; insert into tilitapahtumat values (pvm, time, siirto, x, t1, t2,...); commit; end transaction; 3 4 1
Esimerkissä jakamattomuus merkitsee sitä, että kaikki 3 tietokantaoperaatiota suoritetaan eikä vain osaa niistä. Miksei sitten suoritettaisi? Käsittelyssä voi sattua häiriöitä missä vaiheessa tahansa. Nämä voivat johtua ulkoisista syistä tai olla tkhj:n itse aiheuttamia, jotta se voisi jatkaa toimintaansa (syntyy lukkiutuma, joka pitää purkaa). Voisi siis käydä siten, että tilin t1 sivu on kirjoitettu levylle ja tilin t2 sivu jää kirjoittamatta tällöin operaatio ei olisi jakamaton. 5 Tietokantatransaktioilta edellyteään 4 perusominaisuutta (ACID vaatimukset): Atomisuus (atomicity): kaikki transaktion tietokantamuutokset suoritetaan tai ei mitään niistä. Eheyden säilyttäminen (consistency): transaktio siirtää tietokannan ehyestä tilasta toiseen ehyeen tilaan. Ehyt tila on tila, jossa tietokantaan liittyvät eheysehdot ovat voimassa. Tämän vaatimuksen toteutuminen on käyttäjän vastuulla. 6 Eristyvyys (isolation) Muut samanaikaiset transaktiot eivät sotke transaktion suoritusta. Transaktio suoritetaan ikään kuin muita samanaikaisia transaktioita ei olisikaan. Transaktion kannalta näyttää siltä, että kaikki muut transaktiot on suoritettu joko ennen sitä tai sen jälkeen. Pysyvyys (durability) Kun tkhj ilmoittaa käyttäjälle, että transaktio on menestyksekkäästi päätetty, sen tekemät muutokset tietokantaan jäävät voimaan (kunnes jokin toinen transaktio ne kumoaa). Mikään häiriö ei niitä hävitä. Yleisesti kannan käsitttely muodostuu luku- ja kirjoitusoperaatioista. read(x,v) lukee tietoalkion X muuttujaan v write(x,v) kirjoita tietoalkio X muuttujasta v E&N: käyttää muotoa (read_item(x), ja write_item(x) 7 8 2
read(x,v): Selvitä X:n sisältävän sivun osoite p. Pyydä puskurienhallintaa lataamaan sivu p (lukee, jos sivu ei ole jo puskurissa). Puskurienhallinta naulitsee sivun puskuriin (fix) ja palauttaa puskurin osoitteen b. Kopioi tietoalkio X puskurista b muuttujaan v. Ilmoita puskurienhallinnalle sivun vapautuksesta (unfix). write(x,v) Selvitä X:n sisältävän sivun osoite p. Pyydä puskurienhallintaa lataamaan sivu p muutosta varten (lukee, jos sivu ei ole jo puskurissa). Puskurienhallinta naulitsee sivun puskuriin (fix) ja palauttaa puskurin osoitteen b. Kopioi tietoalkio X muuttujasta v puskuriin b. Ilmoita puskurienhallinnalle sivun muuttamisesta ja vapautuksesta (unfix). Puskurienhallinta kirjoittaa aikanaan muuttuneet sivut levylle. Sekä lukuun että kirjoitukseen liittyy yllä kuvatun lisäksi samanaikaisuuden hallintaan liittyviä toimia (lukituksia). 9 10 Esim: tililtä nosto Sarjallinen suoritus sama proseduuri tilinosto(x, summa) : (X = tietyn tilin saldo tili-relaatiossa, alussa esim. 2000) T1 read(x, v) v:=v-500; write(x, v) T2 read(x, u) u:=u-1000; write(x, u) Jos T1 ja T2 alkavat suunnilleen samaan aikaan ja etenevät vuorotellen huonossa kontrollissa, voi tilin X saldo olla lopussa 1500, 1000 tai 500 (oikea). 11 Kontrolloitu suoritus olisi sarjallinen suoritus (serial schedule), jossa joko T1 suoritetaan kokonaan ennen kuin T2 aloitetaan tai päinvastoin. Tällöin lopputulos molempien jälkeen on 500, mutta välitulos on joko 1000 tai 1500. Yleisesti sarjallinen suoritus aiheuttaa joidenkin transaktioiden huomattavaa viivästymistä ja saattaa jopa estää suorituksen (edellinen epäonnistuu, joten seuraavaa ei voida aloittaa). 12 3
Sarjallinen ajoitus Esimerkki Tilisiirto Esim. sarjallinen ajoitusjärjestys: T1: read (x1, v1) : T1: write(x1, v2) T1: commit; T2: read(x2, u2) T2: read(x1, u1) T2: write(x1, u3) T2: commit; Transaktiojoukon jokainen sarjallinen ajoitusjärjestys on oikea; vrt. eristyvyyden määrittely. Tarkastellaan tilisiirto (1234, 5678, 5000) operaatiota: 1. transaktion aloitus(pyyntö) begin 2. lukuoperaatioita tietohakemiston sivuihin 3. lukuoperaatioita tili-relaation hakemistoon 4. luetaan puskuriin b se tili-relaation sivu p, jossa on monikko 1234 --- ohjelmallisesti varsinainen tililtäotto ja siihen mahdollisesti liittyvät tarkistukset + lisätoimet --- 5. kirjoitetaan muutetun monikon sisältö sivulle p 6.-9. vastaavat operaatiot tilin 5678 sivulle (tilillepano) 10. lukuoperaatioita tietohakemistoon 11. luetaan tilitapahtuma-relaation viimeinen sivu (oletusrakenne kasa) 12. lisätään tilitapahtuma tietue tilitapahtumasivulle 13. transaktion sitoutumispyyntö (commit). 13 14 Riippumattomuus Rinnakkaisuus Huom. Kahden sarjallisen ajoituksen tulos ei ole aina sama, jos tapahtumat eivät ole toisistaan riippumattomia. Esim: T1=Lyhennys(Laina,s): read(laina,v), write(laina,v-s) T2=Lisääkorko(Laina,p): read(laina,u), write(laina,u+p*u) Olkoon alkuarvo 2000, s=500, p=0.1 Lyhennys(Laina,500), Lisääkorko(Laina,0.1) antaa tuloksen 1650 ja Lisääkorko(Laina,0.1), Lyhennys(Laina,500) antaa tuloksen 1700. Rinnakkaisessa ajoituksessa on ainakin jokin vaihe, jossa on samanaikaisesti kesken enemmän kuin yksi transaktio. Rinnakkaisia ajoituksia on tyypillisesti monia: transaktion vuoro voi päättyä melkein missä kohdassa tahansa (käytännössä esim. siirräntäoperaation kohdalla; vrt. käyttöjärjestelmätason prosessinhallinta). Rinnakkainen ajoitus on oikea, jos se on ekvivalentti jonkin sarjallisen ajoituksen kanssa. Ajoitusta kutsutaan tällöin sarjallistuvaksi (serializable) 15 16 4
Konfliktiekvivalenssi Ekvivalenssi voidaan määritellä eri tavoilla, mutta yleisesti käytetään konfliktiekvivalenssia, jossa keskenään konfliktoivien operaatioiden järjestys on sama kuin sarjallisessa ajoituksessa Operaatiot konfliktoivat, jos 1) ne kuuluvat eri transaktioihin, 2) ne kohdistuvat samaan tietoalkioon, ja 3) ainakin toinen operaatio on write-operaatio. Operaatioiden järjestys Esimerkiksi ajoitus T1: read(x1, v1) T2: read(x2, v2) T1: write(x1, v3) T1: commit; T2: read(x1, v4) T2: write(x1, v5) T2: commit; on konfliktiekvivalentti ajoituksen (T1; T2), mutta ei ajoituksen (T2; T1) kanssa 17 18 Eristyvyysanomaliat Haamut Eristyvyysrikkomuksissa on kolme päätyyppiä eli eristyvyysanomaliaa: 1 likainen kirjoitus (dirty write) transaktio kirjoittaa toisen, sitoutumattoman, transaktion kirjoittaman tietoalkion päälle 2 likainen luku (dirty read) transaktio lukee likaisen eli ei-pysyvän tietoalkion arvon (sitoutumattoman kirjoittaman arvon) 3 toistokelvoton luku (unrepeatable read) toinen transaktio kirjoittaa T1:n lukeman tietoalkion ennen kuin T1 sitoutuu Haamu: erikoistapaus, joka syntyy, kun tietokantaan lisätään transaktiossa T rivi, joka täyttää toisen transaktion S käsittelemien rivien valintaehdon. Esim. T: insert into employee values (, dno=5) S: select sum(salary) where dno=5 Ajoitus (T,S) ottaa mukaan myös uuden työntekijän palkan, ajoitus (S,T) sitä vastoin ei. Jos S ehtii ottaa relaation käyttöönsä ennen lisäystä, kesken laskennan ilmestyvä rivi on ns. haamu. 19 20 5
Eristyvyyden takaamismenetelmiä Asettamalla tietoalkioille lukkoja (locks): operointi on sallittu vain transaktiolle, joka on saanut haltuunsa tietoalkion lukon (käyttöoikeuden). Seuraamalla transaktioiden ajoitusta niihin liittyvien aikaleimojen (timestamp) avulla. Ylläpitämällä tietoalkioiden useita arvoja ( vanha ja uusi ): moniversiotekniikalla. Optimistisilla menetelmillä: antamalla transaktioiden suorittaa varsinaiset operaationsa ja tarkistamalla sitten validointivaiheessa, ettei suoritukseen sisälly ristiriitaisia tilanteita. Operaatiot kohdistuvat tietoalkioiden tilapäisiin kopioihin, joten menetelmä on tavallaan moniversioinen. Lukitus on yleisimmin käytössä. 21 Lukitusmenetelmä Lukko (lock) on tietoalkion käyttöä valvova muuttuja. Lukulukko (read lock; shared lock) antaa oikeuden lukea tietoalkion, mutta ei kirjoittaa sitä. Usealla transaktiolla voi samanaikaisesti olla lukulukko tiettyyn tietoalkioon (lukuoperaatiot eivät häiritse toisiaan, shared ) Kirjoituslukko (write lock, exclusive lock) antaa oikeuden kirjoittaa (ja lukea) tietoalkion arvon. Kirjoituslukko on poissulkeva eli vain yhdellä transaktiolla voi olla samanaikaisesti kirjoituslukko tietoalkioon X. Muilla transaktioilla ei voi olla edes lukulukkoa tietoalkioon X. 22 Lukko-operaatiot Lukkojen käyttöön liittyviä operaatioita read_lock(x): lukulukon pyyntö write_lock(x): kirjoituslukon pyyntö unlock(x): X:n lukon vapautus Lukkoja valvoo tkhj:n lukonhallitsin (lock manager). Transaktiolla on enintään yksi lukko tietoalkioon kerrallaan. Lukot vapautetaan viimeistään sitoutumisen yhteydessä Tietorakenteita Lukkojen hallinnan keskeiset tietorakenteet: lukkotaulu (lock table), joka on organisoitu tietoalkiokohtaisesti: Tietoalkion X tunniste. X:n lukon haltijoiden tiedot: transaktion tunniste ja lukon tyyppi. X:n lukkoa haluavien transaktioiden jono. Lukkotaulusta saadaan nopeasti selville tietoalkion lukituksen tilanne. Organisointina voi olla esim. hajautusrakenne. transaktiotaulu Jokaiselle transaktiolle tietue, josta alkaa transaktion hallussa olevien lukkojen (tietoalkioiden tunnisteiden) ketju. Taulun avulla löydetään transaktion sitoutuessa tai peruuntuessa sen hallussa mahdollisesti olevat lukot, jotka on vapautettava. 23 24 6
Read_lock(X) read_lock(x): ( rl(x) = X:n lukulukkojen määrä ) 1. hae X:ää lukkotaulusta 2. jos X ei ole taulussa, vie X tauluun, aseta rl(x) := 0 ja mene askeleeseen 5 3. jos transaktiolla T on jo lukko X:ään, palaa 4. jos jollakin toisella transaktiolla on jo kirjoituslukko X:ään, aseta T lukon vapautumista odottavien transaktioiden jonoon lukkotaulussa. 5. kirjaa lukkotauluun T:lle lukulukko X:ään (ja liitä X T:n lukkojen ketjuun transaktiotaulussa), aseta rl(x) := rl(x) +.1 Write_lock(x) write_lock(x): 1. hae X:ää lukkotaulusta. 2. jos X ei ole taulussa, vie X tauluun ja mene askeleeseen 5 3. jos transaktiolla T on jo kirjoituslukko X:ään, palaa 4. jos jollakin toisella transaktiolla on jo luku- tai kirjoituslukko X:ään, aseta T lukon vapautumista odottavien transaktioiden jonoon. 5. jos T:llä on jo lukulukko X:ään, korota (upgrade) se kirjoituslukoksi ja aseta rl(x) := 0; muuten kirjaa T:lle uusi lukko, kirjoituslukko, lukkotauluun. 25 26 Unlock(x) 2PL unlock(x): 1. etsi X:ää vastaava tietue lukkotaulusta 2. jos X:ään on kirjoituslukko transaktiolla T, poista T lukonhaltijain joukosta ja, jos jonossa on odottavia transaktioita, herätä niistä ensimmäinen muuten (T:llä on lukulukko) aseta rl(x) := rl(x) - 1; Jos rl(x) = 0, herätä ensimmäinen odottavista transaktioista, jos niitä on 3. jos odottavien jono oli tyhjä, poista X:n tietue lukkotaulusta Kaksivaiheinen lukituskäytäntö (2PL, two-phase locking) Kaksivaiheisen lukituksen perusajatus: mitään lukkoa ei vapauteta ennen kuin kaikki transaktion tarvitsemat lukot on varattu. Transaktio jakaantuu kahteen vaiheeseen: Kasvuvaiheeseen, jonka aikana kaikki lukot varataan aina kun operaatio tehdään, Lukulukon korotus kirjoituslukoksi tulkitaan lukon varaukseksi eli on tehtävä kasvuvaiheen aikana. Kutistumisvaiheeseen, jonka aikana lukot vapautetaan. 27 28 7
- samanaikaisuus Esim. seuraavat transaktiot toteuttavat 2PL-ehdon: T1 T2 read_lock(y); read_lock(x); read(y); read(x); write_lock(x); write_lock(y); unlock(y); unlock(x); read(x); read(y); X:= X + Y; Y:= X + Y; write(x); write(y); commit; commit; unlock(x); unlock(y); Ankara 2PL Ankara 2PL (Strict Two-phase Locking, Strict 2PL): Jokaisen transaktion tulee pyytää lukulukko objektiin ennen lukemista ja kirjoituslukko ennen kirjoitusta. Transaktio pitää kaikki lukot sitoutumiseen asti, jonka yhteydessä kaikki lukot vapautetaan. 29 30 Ankara 2PL Ankara 2PL 1 likainen kirjoitus Esim T1: write_lock(x); T1: write(x, u); T2: write_lock(x); T2: write(x, v); T1: commit; (tai rollback;) T1: unlock(x); Ajoitus ei ole mahdollinen, koska T2 ei voi saada X:n kirjoituslukkoa eikä siten tehdä likaista kirjoitusta. Olennaista on, että T1:n kirjoituslukko on pitkäaikainen; 2 likainen luku Esim. T1: write_lock(x); T1: write(x, u); T2: read_lock(x); T2: read(x, v); T1: commit; (tai rollback;) T1: unlock(x); T2 ei voi saada edes lukulukkoa, kun X:llä on pitkäaikainen kirjoituslukko. On helppo nähdä, että ankara 2PL rajoittaa usein liian paljon samanaikaisuutta: tietyn tietoalkion lukko voitaisiin vapauttaa heti, kun siihen kohdistuvat operaatiot on tehty. 31 32 8
Ankara 2PL 3 toistokelvoton luku Esim. T1: read_lock(x); T1: read(x, u); T2: write_lock(x); T2: write(x, v); T1: commit; (tai: rollback;) T1: unlock(x);... Toistokelvoton luku estyy: T2 ei voi saada kirjoituslukkoa X:ään eli ei voi muuttaa X:n arvoa, kun T1:llä on pitkäaikainen lukulukko. (Lyhytaikainen lukulukko ei riitä.) Transaktion sisäisiä vaiheita hallitaan määrittelemällä transaktion tilasiirtymämalli ja seuraavat tilat: 1. alkutila: transaktio syntyy (generoidaan) - oma tunniste, jne. 2. aktiivinen: transaktio suorittaa varsinaisia operaatioitaan (read(), write() ) 3. osittain sitoutunut (partially committed): transaktion ohjelmakoodi on suoritettu ja se on pyytänyt sitoutumista (commit-operaatiolla) 33 34 4. sitoutunut (committed): tkhj on vahvistanut transaktion tietokantaan tekemät muutokset pysyviksi eli sitoutuminen on onnistunut Tietokannan muutokset eivät ole enää peruttavissa (ilman uutta transaktiota). Tiedot eivät kuitenkaan ole välttämättä levyllä asti (vahvistus vain looginen ), mutta vastaava lokitieto on levyllä 5. epäonnistunut (failed): sitoutuminen on epäonnistunut (esim. samanaikaisuuden hallintaan liittyvien tarkistusten takia) tai transaktio itse on suorittanut keskeytysoperaation rollback (abort) 6. keskeytetty: epäonnistunut transaktio on peruutettu eli tietokanta on palautettu ennen transaktion aloitusta vallinneeseen tilaan 7. päättynyt (terminated): transaktion olemassaolo lakkaa 35 36 9
Keskeytetty transaktio voidaan aloitaa uudelleen päättynyttä ei voida. Alla tilasiirtymäkaavio: alkutila uudelleenaloitus begin keskeytetty aktiivinen rollback pyyntö epäonnistunut rollback toteutus read, write commit pyyntö commit hylätty osittain sitoutunut commit hyväksytty sitoutunut päättynyt 37 SQL:ssä transaktio päätetään normaalisti commitlauseella. Samalla commit aloittaa uuden transaktion. Joissain ympäristöissä (esim. sulautettu SQL) voi käyttää myös begin transaction ja end transaction lauseita. Transaktion voi lopettaa myös rollback-lauseella. Rollback peruuttaa pääsääntöisesti koko transaktion. Osittainen peruutus saatavissa aikaan jatkoaloituskohdilla (savepoint). 38 commit; muutoksia 1 /* tallenna tilanne, nimeä kohta = p1 */ savepoint p1; muutoksia 2 /*peruuta transaktiossa pisteeseen p1, muutoksia 1 säilyy */ rollback to savepoint p1; 39 Ajoittaja Samanaikaisten toimintojen salliminen ja häiriöiden vaikutusten eliminointi muodostavat monimutkaisen toiminnallisen kokonaisuuden, joka on ajoittajan tehtävänä. Pelisääntönä on karkeasti se, että samanaikaisuutta rajoitetaan vain niin paljon, ettei normaalitilanteessa jouduta usein korjaamaan sen aiheuttamia (tulossa olevia) vaurioita. Rajoituskeinoja: lukitaan tietoalkioita muilta lukoilla (lock); pitkä lukinta-aika rajoittaa hankalasti muiden transaktioiden etenemistä; voi syntyä lukkiutuma, jolloin mikään transaktio ei pääse etenemään optimistisia menetelmiä: annetaan mennä; tarkistetaan; korjataan, jos tarpeen 40 10
Sarjallistuvuusteoria Sarjallistuvuusteoria on matemaattinen menetelmä, jonka avulla voidaan todistaa toimiiko ajoittaja oikein. Ajoittaja on se ohjelmapalikka, jonka tehtävänä on rinnakkaisten operaatioiden suorittaminen. Käytännössä rinnakkaiset transaktioiden suoritukset kuvataan historian avulla. Teoria antaa täsmälliset ominaisuudet, jotka historian tulee täyttää jotta se olisi sarjallistuva. Transaktio Transaktio T i koostuu operaatioista, joita ovat lukuja kirjoitusoperaatiot sekä joko peruutusoperaatio tai sitoutumisoperaatio siten että luku- ja kirjoitusoperaatiot edeltävät peruutus- tai sitoutumisoperaatiota. Jos transaktio T i sisältää sekä luku- että kirjoitusoperaation samaan tietoalkioon, on operaatioille määritelty järjestys. Eli joko lukuoperaatio ennen kirjoitusoperaatiota tai päinvastoin. 41 42 Historia Operaatiot Historia kuvaa missä järjestyksessä operaatiot on suoritettu. Koska osa operaatioista voidaan suorittaa rinnakkain, historia määritellään osittaisena järjestyksenä. Transaktio määrittelee omien operaatioidensa järjestyksen. Lisäksi historian tulee määritellä konfliktoivien operaatioiden järjestyksen. Kaksi operaatiota sanotaan olevan konfliktissa keskenään jos ne operoivat samaan tietoalkioon ja vähintään toinen on kirjoitusoperaatio. b i :Transaktio i alkaa. r i [x]: Transaktio i lukee tietoalkion i arvon. w i [x]: Transaktio i kirjoittaa uuden arvon tietoalkioon i. c i : Transaktio i sitoutuu. a i : Transaktio i peruuntuu. 43 44 11
Esimerkki T 1 = r 1 [x] w 1 [x] c 1 T 3 = r 3 [x] w 3 [y] w 3 [x] c 3 T 4 = r 4 [y] w 4 [x] w 4 [y] w 4 [z] c 4 {T 1,T 3,T 4 }:n täydellinen historia on: H 1 = r 1 [x] w 1 [x] c 1 r 4 [y] w 4 [x] w 4 [y] w 4 [z] c 4 r 3 [x] w 3 [y] w 3 [x] c 3. Sarjallistuvat historiat Historia on sarjallinen jos jokaiselle transaktioparille T i ja T j historiassa H, joko kaikki T i :n operaatiot esiintyvät yhtäkään T j :n operaatiota tai päinvastoin. Historia on sarjallistuva jos täydellinen historia on ekvivalentti jonkin sarjallisen historian kanssa. Historian sarjallistuvuutta voidaan tutkia muodostamalla historiasta sarjallistuvuusverkko. 45 46 Sarjallistuvuusverkko Esimerkki Olkoon H historia tapahtumajoukolle T = {T 1,T 2,,T n }. Sarjallistuvuusverkko H:lle, merkitään SG(H), on suunnattu verkko, jonka solmuina ovat kaikki transaktiot T:stä ja särminä ovat kaikki T i < T j (i < j) siten että yksi T i :n operaatio edeltää ja konfliktoi yhtä T j :n operaatiota H:ssa. H 1 = r 2 [x] r 1 [x] w 1 [x] r 3 [x] w 2 [y] c 2 w 1 [y] w 3 [x] c 3 c 1 47 SGH(H 1 ) =T 2 < T 1 < T 3 48 12
Esimerkki 2 Ajoitus, joka ei ole sarjallistuva: T1: R(A), W(A), R(B), W(B) T2: R(A), W(A), R(B), W(B) T1 A T2 Sarjallistuvuusverkko B 49 13