Tranzakció kezelés

A előző fejezetekben a negyedik generációs nyelvek használatát tekintettük át. Ennek során megismerhettük azon alapfogalmakat, melyek segíthetnek benünket az adatbázisokra épülő alkalmazások fejlesztésében. A következő fejezetekben egy váltással átugrunk az adatbáziskezelés egy másik területére, amely a relációs adatbáziskezelők belső működését érintik. Ennek során beletekintünk az RDBMS legfontosabb komponenseinek működésébe, az egyes komponensek elvi felépítésébe. Ezek az ismeretek sokunk számára ugyan nem lesznek olyan kézelfoghatóan és közvetlenül hasznosíthatóak, mint az alkalmazás fejlesztő eszközök terén nyert tapasztalatok, de ez a terület számos olyan lényeges háttérinformációt nyujt az alkalmazás fejlesztő számára is, melyek igen hasznosnak bizonyulhatnak az adatbázisok kezelése terén is. Könnyen belátható, hogy az RDBMS belső működési alapjainak és a működést szabályzó utasítások ismerete nélkül nem tudunk számos, a gyakorlati életben fellépő speciális igényt megoldani. Hiszen ahhoz, hogy tudjuk, hogy például milyen csapdák leselkednek egy többfelhasználós rendszerben, vagy hogyan lehet biztosítani, hogy a programunk futása során ne változzon meg a látott adatbázis képe, azaz programunk mindvégig ugyanazon adattartalmat lássa, ahhoz ismerni kell, hogyan oldják meg a párhuzamos hozzáférések nehézségeit az RDBMS rendszerekben. A párhuzamos hozzáférések kezelése mellett az adatok védelme illetve a műveletek végrehajtásának mechanizmusa is olyan, az adatbáziskezelés speciális területe, melyekre a következőkben kitérünk. Mint már korábban tapasztalhattuk, a nagyobb adatbáziskezelő rendszerek, mint például az Oracle, több felhasználó párhuzamos munkáját is lehetővé teszi, vagyis egyszerre, egyidejűleg többen is használhatják ugyanazt az erőforrást, jelen esetben az adatbázist. A köznapi tapasztalatainkban is gyakran találkozhatunk ilyen, ugynevezett osztott erőforrásokkal. Gondoljunk csak például a tanszéki nyomtatóra, vagy például egy éttermi asztalra. Az erőforrások osztott használata az informatika más területén is lényeges szerepet kap, felidézve az operációs rendszerek tantárgyat számos fogalom feleleveníthődhet e témakörből, melyek itt is segítséget adhatnak. Az osztott hozzáférésnek számos előnye van a felhasználó számára, hiszen nem kell több példányban megismételni az erőforrást minden felhasználónál, elegendő egyetlen egy példányban megvalósítani. Az osztott hozzáférés biztosítása nélkül például vagy csak egy felhasználó, ügyfél dolgozhatna a rendelés állománnyal egy vállaltnál, vagy minden ügyfélhez létre kellene hozni egy saját egyedi állományt. Ez utóbbi esetben viszont a teljes rendelés tétel együttes kezelése jelentene gondot. A párhuzamosság léte nélkül sokkal lasabbak lennének az adminisztrációs, adatkezelési műveletek. A párhuzamosság tehát hasznos és kényelmes dolog. legalábbis a felhasználó oldaláról tekintve. A fejlesztő oldaláról nézve a párhuzamosság kihívást is jelent, hiszen a konkurrens műveletek engedélyezésével számos probléma is felmerül a műveletek végrehajtása során. Konfliktus helyzet áll elő, ha például két jelentkező is igényt tart egyidejűleg ugyanarra az erőforrásra. E konfliktus kezelésének legismertebbb formája, hogy a két igénylő közül az egyiket várokoztatjuk. Ilyen várakozósorok alakulhatnak ki például a nyomtatók használatakor is, melyben a kinyomtatandó állománok várakoznak a sorrakerülésükre. A várakozósorok kezelésekor a várakoztatások mechanizmusa, algoritmusa jelent lényeges elemet az osztott erőforrások működésében. Ugyan az adatbáziskezelés területén is lényeges szerepe van a várakoztatás mechanizmusának, emellett azonban számos egyedi vonása is létezik, melyekre itt kitérünk, és amelyek miatt ilyen itt külön fejezetben foglalkozunk e témával. Az adatbáziskezelésben lezajló folyamatok, műveletek számos lényeges egyedi sajátsággal rendelkeznek, melyek alapján eltérő módon kell őket kezelni a hagyományos operációs rendszerbeli műveletektől. E sajátságok a következőkben foglalhatók össze: - Az adatbázisban a vizsgált feladatkör olyan leírása található, melynek ki kell elégítenie minden megadott integritási feltételt. Vagyis az adatbázisnak konzisztens állapotban kell megmutatkoznia minden felhasználó előtt. Egy ilyen integritási feltétel lehet például, hogy a vállalatnál azok kapnak rendszeres segélyt, akiknek három gyerük van. Vagyis, ha a dolgozó gyerekeinek darabszáma háromra változik, be kell állítani a segélyjelző attributumot. Ez a mechanizmus megoldható például a már ismert trigger koncepcióval. Ekkor előbb bekövetkezik a kiváltó esemény, majd ennek hatására a választevékenység. A helyes működésben akkor keletkezhet zavar, ha az RDBMS nem tudja elvégezni például a kijelölt választevékenységet egy rendszerösszeomlás miatt. Ekkor a gyereklétszám három értékű, de a segély nincs kijelölve, vagyis az adatbázis inkonzisztens állapotban maradt. Ez természtesen nem megengedhető, hiszen ezáltal az egész adatbázis hitele elvész, emiatt az RDBMS-nek gondoskodnia a konzisztencia fenntartásáról. Hasonlóan az RDBMS kötelessége, hogy megakadályozzon minden olyan közvetlen műveletet is, melyek a konzisztencia megsértésével járnának. Így például nem engedhető meg, hogy az ügyfél életkor mezőjébe 2367 évet vigyünk fel, hiszen ez ellent mond a valós tapasztalatainknak. - Az adatbázisban történő munka másik sajátossága, hogy a felhasználó, alkalamzói program által kiadott műveletek rendszerint nem izolált, egyedi műveletek, hanem szoros kapcsolat van közöttük. Vagyis nem egy-egy független műveletet, hanem egy műveletsort kíván a felhasználó végrehajtani, melyek természetesen több elemi műveletből állhatnak. Gondoljunk például arra az esetre, amikor például egy új tantágyblokkot viszünk egy hallgatói nyilvántartási rendszerbe. Az egy tantárgyblokkra vonatkozó információk valószínűleg több táblában helyezkednek el, hiszen külön kell tárolni a tárgyblokk megnevezését, leírását, illetve az blokkhoz tartozó tárgyak listáját. Így több egymástkövető INSERT utasítás alkot egy egységes műveltsort. A felhasználó számára e műveletek együttesének van értelme, a különálló műveletek önmagukban ismétcsak hibás, ellentmondásos eredményt okoznak. Ez a követelmény többen között az igénnyel is jár, hogy az RDBMS-nek képesnek kell lennie a műveletsorből eddig elvégzett műveletek visszagörgetésére, ha menetközben valahol elakadna a kijelölt műveltsor, s nem lehetne tovább haladni a végrehajtásban. Félig elvégzett műveletsor nem maradhat a rendszerben. - A felhasználót arról is biztosítani kell, hogy ha visszajelzést kapott a műveletsor sikeres végrehajtásáról, akkor biztos lehet abban, hogy a műveletsor hatása véglegesen beleíródott az adatbázisba, s esetleg később jövő művelet nem törölheti azt már vissza. Az RDBMS-nek tehát tartania kell a szavát, ha már egyszer elvégzettenk tekintett egy műveletsort. A véglegesség elve közvetve még azt az igényt is magába foglalja, hogy a lezárt tranzakciók eredményeit az adatbáziskezelő minden áron őrizze meg, gondoskodjon a véglegesített adatok elvesztése elleni védelemről. A felhasználó biztos akar lenni abban, hogy az egyszer lementett adatok később bármikor elérhetők lesznek. - Ugyancsak a felhasználó jogos elvárása, hogy a párhuzamosság káros mellékhatásaiból minnél kevesebbet érezzen. Vagyis az adatbázis ne egy olyan fazékhoz hasonlítson, melybe a tűzhely körül álló minden szakács menetközben konkurensen dolgozva beleteheti a saját receptje szerint szükséges hozzávalókat, s ezáltal az elkészült étel minősége, jellege attól függ, hogy hány szakács dolgozott még vele párhuzamosan, s hogy azok éppen milyen tevékenységet végeztek. A felhasználó számára az a legjobb és legbiztosabb megoldás, ha úgy érzi, hogy csak egyedül ő dolgozik a rendszeren, s munkájának eredménye csak a kiinduló állapottól függ, s nem attól, hogy milyen egyéb tevékenységek folynak még mellette. A felmerült igények kielégítése megköveteli, hogy a DBMS maga vegye felügyelete alá a műveletek végrehajtását, gondoskodva a műveletek megflelő sorrendben és megfelelő módon történő elvégzéséről. E terület az adatbáziskezelés egyik fontos és igen széles körben vizsgált területévé vált, létrehozva a sajátos terminológiákat s módszereket. A következőkben a DBMS rendszerek műveletvégrehajtási műhelyébe pillantunk bele. Tranzakció és history fogalma Rögtön az elején be kell vezetnünk egy új és alapvető fogalmat, a tranzakció fogalmát. A DBMS rendszerekben ugyanis, az elvégzendő műveletek speciális jellegét hangsúlyozva, külön fogalmat vezetnek be a kezelt műveletsorra. E fogalom, a tranzakció, amellyel a logikailag összetartozó és egységként kezelt műveletsort szokás jelölni. A tranzakció az adatbáziskezelés központi fogalma, hiszen a DBS minden művelete tranzakciókba szervezetten hajtódik végre (legalábbis az igazi DBMS rendszerekben). Ennek megfelelően a DBMS nem engedi a tranzakción kívüli műveletvégrehajtást sem. Mivel a művceletek végrehajtásának vezérlése e őj fogalomhasználattal a tranzakciók vezérlését, kezélést jelenti, ezért a DBMS elmélet e szeletét szokás a tranzakciókezelés területének is nevezni. Minden bejelentkezésünkkor, anélkül, hogy erre explicite külön parancsot kiadnánk, elindul egy új tranzakció, mely egészen addig tart míg ki nem lépünk a rendszerből, vagy le nem állítjuk a futó tranzakciót az erre szolgáló COMMIT vagy ABORT SQL utasításokkal. A tranzakció leállítása utáni esetlegesen következő első parancsunk, melynek nem kell feltétlenül egy tranzakció indító parancsnak lennei, elindít egy újabb parancsot. Tehát a tranzakciók ott vannak, anélkül hogy külön utasítással létre kellene hoznunk őket, a tranzakciók nem kerülhetők ki. Ez a megköttés biztosítja azonban, hogy a műveletvégrehajtás mindíg a tranzakció kezelő felügyelete alatt történik, s így mindíg biztosított a felálllított követelmények betartatása. A tranzakcióknak tehát úgy kell végrehajtódniuk, hogy megfeleljenek az adatbáziskezelés sajátosságainak. E sajátosságokat előbb már részletesebben elemeztük, így most csdak röviden megemlítjük őket. A tranzakcióknak úgy kell tehát végrehajtódniuk, hogy a tranzakció legyen - atomi, vagyis vagy minden művelete végrehajtódik, vagy egy sem. - konsisztens, vagyis konzisztens állapotból konzisztens állapotba vigyen. - izolált, vagyis az eredmény ne függjön más párhuzamosan futó t ranzakció hatásától - tartós, vagyis a véglegesített tranzakció hatása már nem szüntethető meg. E követelményrendszer elvárásait, az egyes követelmények angol megfelelőihez tartozó kezdőbetűk alapján szokás ACID-elveknek is nevezni (A:atomicity, C:consistency,I:isolation, D:durability). A DBS-ek egyik fő feladata tehát abban áll, hogy biztosítsák az ACID elveknek megfelelő működést. Ez azonban nem is olyan egyszerű feladat, mint az látni is fogjuk. A tranzakció kezelés módszereinek áttekintéséhez előbb magát a tranzakciót visgáljuk meg közelebbről. Az elemzéshez olyan formalizmust célszerű keresni, amely nem túl bonyolult és alkalmas a lényeg kiemelésére. A tranzakciók leírásánál a hozzá tartozó műveletsort kell megadni. Egy RDBMS tranzakció ugyan igen sokféle SQL utasításból állhat, az egyszerűség végett azonban mi nem a konkrét utasításokat használjuk a leírásnál, hanem egy tömörebb jelölésmódot vezetünk be. Ehhez abból indulunk ki, hogy az DB szempontjából a műveletnél az a fontos, hogy megváltozik-e a DB tartalma vagy sem. Ennek megfelelően beszélünk olvasási (r) és írási (w) műveletekről. Mivel a műveletknél azis fontos, hogy mely DB objektumra hatnak, ezért a művelet megadásánál az objektumit is feltüntetjük paraméterként, pl. r(x) ahol r az olvasás és x az objektum azonosító jele. A tranzakciók műveletinél az írás és olvasás műveletei mellett még két fontos utasítást kell figyelembe venni, melyek igen lényegesek az tranzakció kezelése szempontjából. Ez a két művelete a tranzakció véglegesítése, a Commit utasítás, melyet a c betővel fogjuk jelölni, s az Abort utasítás, melynek az a betű lesz a szimbóluma. Az a művelet esetén a tranzakció minden műveletét vissza kell játszani, mintha meg sem történtek volna. A tranzakció hatása azonban nemcsak attól függ, hogy milyen műveleteket hajtott végre, hanem attól is, hogy azokat milyen sorrendben hajtotta végre. Tapasztalatból tudjuk, hogy nem ugyanazon hatással jár, ha előbb rálépünk valaki lábára, majd azt mondjuk, hogy 'pardon!', mintha előbb azt mondjuk, hogy 'pardon!', majd rálépünk a lábára. Emiatt a tranzakciók leírásánál is figyelembe kell venni a műveletek közötti végrehajtási sorrendet. Ehhez bevezetünk egy megelőzési relációt a műveletek között. A reláció jele: ->. Adott p1 és p2 műveletek esetén p1 -> p2 teljesül, ha p1 előbb következik be, mint p2. A tranzakció tehát olyan gráffal írható le, melynek csomópontjai a műveletek, irányított élei a megelőzési relációk. A gráf terjedelmének csökkentésére felhasználhatjuk a megelőzési reláció tranzitív voltát. A különböző tranzakciókhoz különböző gráf fog tartozni. A gráf ábrázolásból kiindulva azonban meg kell mondani, hogy nem minden gráf jelent egy létező tranzakciót. Vannak ugyanis olyan megkötöttségek, melyeket ki kell elégíteni egy érvényes tranzakciónak: - A gráfnak tartalmaznia kell egy lezárási műveletet, tehát vagy egy c vagy egy a elemet. Mivel egy e két művelet bármelyike lezárja a tranzakciót, ezért pontosan csak az egyikük szerepelhet a gráfban. - A lezárási művelet a tranzakció utólsó művelete, ezért minden más művelet megelőzi őt. A lezárási feltételek mellett, melyek szükségessége könnyen belátható, a gráfnak egyfajta egyértelmű végrehajthatósági feltételt is eleget kell tennie. Ezalatt azt értjük, hogy a gráf alapján elvégezhessük a műveletsort, melynek eredménye, azonos kezdőfeltételek mellett mindíg megegyezik a leírt tranzakció eredményével. A következő gráf például nem elígiti ki ezen feltételt, habár a lezárási kritériumoknak eleget tesz: A gráf hibája, hogy létezik olyan különböző végrehajtási sorrend, melyek mindegyike megfelel a gráf követelményeinek, de erredményük mégis különböző lesz. Így például, ha w(x) 3-at, w'(x) 6-ot ír ki az x objektumba, akkor w(x) -> w'(x) illetve w'(x) -> w(x) egész más végeredményt fog szolgáltatni. A megadott gráf tehát túl keveset mond a műveletek sorrendjéről. Ebből azt a következtetést vonhatjuk le, hogy olyan gráfot engedhetünk csak meg, melyekben bármely két művelet között létezik, esetleg tranzitív módon megelőzési reláció. Ez a megoldás valóban mindíg egyértelművé tenné a végrehajtást, azonban nem szoktak ennyire éles követelmént megfogalmazni, aminek két fő oka, hogy a felhasználó számára csak a tranzakció eredménye a fontos, a közbenső állapototok érdektelenek. Ha tehát létezik több olyan közbenső átmeneti út, melyek mindegyike ugyanazon eredményhez vezet, akkor a rendszer bármelyiken átmehet a kiinduló pontból a végpontba. Hogy ez valóban előfordulhat, nézzük az alábbi példát. r(x) -> w(x) -> c ^ | r(y) -> w(y) Ebben a gráfban nem ismerjük r(x) és r(y) vagy w(x) és w(y) megelőzési relációját, viszont bármilyen sorrendet véve is, az eredmény mindíg ugyanaz lesz, ha betartjuk a gráfban megadott sorrendiséget. A gráfban megadott sorrendiség modellezhet például egy párhuzamos architektúrán futó DBMS rendszerbeli tranzakciót, hiszen ott a tranzakciók egyes szeleteit szét lehet osztani különböző erőforrásokra, melyekben az egyes részletek időbeli ütemzése az egység feladat, s nem adható meg pontosan. Tehát létezhetnek olyan gráfok, melyekben bizonyos elemek közötti sorrendiség nem szerepel, s nem is határozható meg. A kérdés most már a következő képpen fogalmazódik meg: mely elemek közötti relációk hagyhatók el, miért rossz az alső, s miért jó a második mintagráf. A válasz abban rejlik, hogy a tranzakcióknál a végeredmény fontos. Így tehát azon elemek közötti relációkat szükséges feltüntetni, melyek sorrendje lényeges a végeredmény szempontjából. Az ilyen műveletpárokat szokás konfliktusban álló műveletpároknak is nevezni. A konfliktusban álló műveletpároknál a sorrendiség hatással van az eredményükre. Mivel két különböző objektumhoz való hozzáférés nincs hatással egymásra, ezért konfliktus esetén azonos objektumnak kell szerepelni mindkét műveletben. Másrészt az olvasások nem módosítják az adatbázist, ezért két olvasási művelet is bármikor felcserélhető egymással. Az írási műveletek azonban már otthagyják nyomukat az adatbázisban ezért ezek időbeli pozicója lényeges. Így azt mondhatjuk, hogy két műveletet akkor nevezünk konfliktusban állónak, ha mindkettő ugyanarra az objektumra vonatkozik, s legalább az egyik írási művelet. A konfliktusban álló műveletpárok fogalmát bevezetve, azt mondhatjuk, hogy a gráf esetén akkor egyértelmű a végrehajtás eredménye, ha a gráfban bármely két konfliktusban álló műveletpár között létezik megelőzési reláció. Tehát a tranzakciók megadásánál megköveteljük, hogy bármely két konfliktusban álló művelet között létezzen (esetleg tranzitív módon) megelőzési reláció. A tranzakcióra vonatkozólag az elmondottatak formálisabb jelölésrendszerrel az alábbiakban foglalhatjuk össze. Tranzakciónak nevezzük, azt a T = (Te, ->) párt, melyben Te a műveletek halmaza és * a megeleőzési reláció, azaz Te = {r(x), w(x'), a, c | x,x' * DB} -> részhalmaza a Te * Te halmaznak (Descartes szorzat) ahol DB jelenti az adatbázisbeli objektumok halmazát, úgy hogy a T tranzakcióra teljesülnek a következők: - a eleme Te akkor és csak akkor, ha c nem eleme Te - minden p eleme Te-re, igaz hogy p -> a, ha a eleme Te és p -> c, ha c eleme Te - minden p1, p2 eleme Te esetén, ha p1 és p2 konfliktusban áll, akkor p1 -> p2 vagy p2 -> p1 A tranzakciók elemzése után látható, hogy mikor tekithetünk két tranzakciót ekvivalensnek, vagyis mikor adja a két tranzakció ugyanazt azt az eredményt. Eszerint két tranzakció akkor tekinthető ekvivalensnek, ha mindkettő ugyanazon elemeket tartalmazza és mindkettőben a konfliktusban álló műveletek köztt ugyanolyan a megelőzési sorrend. E két feltétel együttesen biztosítja, hogy azonos induló állapotból ugyanazon műveletek és az eredmény szempontjából ekvivalens sorrendben hajtódjanak végre. A tranzakció fogalmának tisztázása után rátérünk a több tranzakciót is tartalmazó rendszerek leírására. Az egyedül futó tranzakció kezelése, tehát amikor csak egyetlen egy tranzakció élhet a rendszerben, nem jelentene problémát, hisz benne a felhasználó jelöli ki a végrehajtás sorrendjét. A problémák akkor jelentkeznek, ha több tranzakció fut együttesen. Az együtt futó tranzakciókból álló rendszer jelölésére is szokás bevezetni egy új fogalmat az adatbáziskezelés területén belül. Ez a fogalom a history, amely tehát a rendszerben futó tranzakciók együttesét jelenti. A history megadása hasonló a trazakciók megadásához, hiszen a history is műveletekből (a tralmazott trazakciókhoz tartozó műveletekből) áll, melyek között továbbra is lényeges a végrehajtási sorrend az eredmény meghatározásánál. Így a history is tartalmaz egymegelőzési relációt az műveleti között. A history megadása hasonló felépítésű gráffal történik, mint a tranzakció gráf. A history gráfban az egyes műveletek megadásánál a művelet jellege, az objektum megadása mellett még a tranzakció azonosítóját is szerepeltetjük, mint művelet indexet. A következő kis ábra egy két tranzakciót tartalmazó historyt mutat be. r1(x) -> w1(x) -> c1 | v r2(x) -> w2(x) -> a2 Míg azonban a history műveleti elemei az alkotó tranzakciók műveleti elemeinek összessége, addig a megelőzési reláció szükségszerűen kibővül új elemekkel. Ennek oka az, hogy a history esetén is olyan megelőzési reláció rendszert kell felépíteni, mely egyértelműen meghatározza a history eredményét. Mivel itt is a konfliktusban álló műveletek közötti végrehajtási sorrend határozza meg a végeredményt, ezért itt is megadható egy olyan kritérium, hogy a history bármely két konfliktusban álló művelete között léteznie kell (akár tranzitív módon) megelőzési relációnak. A history-k megadásához itt is felhasználhatjuk ugyanazt a formalizmust, mint amit a tranzakcióknál alkalmaztunk. History-nak nevezzük, azt a H = (He, ->) párt, melyben He a history-hoz tartozó műveletek és * a megelőzési reláció, azaz He = uniója a Te halamzoknak -> magába foglalja a Te-k felett értelmezett -> halmazok unióját ahol a megelőzési reláció tartalmazza a tranzakciók megelőzési relációinak unióját, s emellett teljesül a következő feltétel - minden p1, p2 eleme H esetén, ahol p1 és p2 konfliktusban áll teljesül, hogy p1 -> p2 vagy p2 -> p1 A history-nál is lényeges, hogy minden konfliktusban álló műveletpár között létezzen megelőzési reláció, különben nem határozható meg egyértelműen a history eredménye. A következő gráf például nem tesz eleget ennek a követelménynek, így nem tekinthető history gráfnak sem. r1(x) -> w1(x) -> c1 ^ | r2(x) -> w2(x) -> w2(y) -> c2 ^ | r3(y) -> w3(x) -> w3(y) -> c3 A gráf hibája, hogy nem tartalmaz megelőzési relációt r2(x) és w3(x) között. A history-k között is több olyan history lehet, melyek azonos feltételekből kiindulva ugyanazon eredményt szolgáltatják. Ezen history-kat ekvivalens history-knak nevezik. Mivel a végeredménynél a konfliktusban álló műveletpárok sorrendje lényeges, és csak az elfogadott tranzakciók hatása marad meg, ezért két history ekvivalensnek mondható, ha - azonos tranzakciókat tartalmaz és - minden nem abortált tranzakciókhoz tartozó, konfliktusban álló műveletek között ugyanolyan irányú megelőzési reláció áll fenn. Az egymással ekvivalens tranzakciók egymással felcserálhetők, hiszen eredményük, amit a felhasználó megkap, ugyanaz. Így a tranzakció kezelő rendszer a felhasználók által végrehajtásra kijelölt history-t egy ekvivalens history-val helyettesítheti, ha annak szükségét látja. Ennek a cserének a felhasználók semmilyen hatását nem fogják látni. A következőkben azt nézzük meg, hogy vajon minden history értelmesen végrehajtható-e, vajon minden history teljesíti-e a felhasználó elvárásait, vagyis biztosítja az ACID elveket. Mint várható a válasz nem, vagyis nem minden history felel meg a követelmények, éppen ezért a tranzakció kezelő rendszernek be kell avatkoznia a megfelelő history kialakításába. Elsőként néhány olyan history-t mutatunk be, melyek valamilyen formában megsértik az ACID elveket, majd az elvek betartatásának módszereit tekintjük át. Indulásként vegyük az alábbi history gráfot: w1(x) -> r1(y) -> w1(y) -> a1 | ^ v | r2(x) -> w2(x) -> c2 Első pillantésra minden rendben lévőnek látszik, hiszen mindkét tranzakciónak létezik lezárása, s a lezáró utasítások minden más tranzakción belüli műveletet megelőznek. Ezenkívül az egyértelműség is biztosított, hiszen bármely két konfliktusban álló művelet között létezik megelőzési reláció. A probléma akkor derül ki, ha lejátszuk a history végrehajtását. A history gráf alapján előbb a T1 tranzakció kiír egy új értéket az z objektumba, majd ezt a T2 tranzakció elovassa, s ez alapján számításokat végez az x objektum újabb értékének meghatározására, melyet ki is ír, s ezután véglegesíti a tranzakció eredményét. A T1 tranzakció még tovább fut a T2 lezárása után is, hiszen még ezután módosítja y értékét, majd leáll. A leállás a T1-nél viszont nem véglegesítést hanem visszagörgetést jelent. Ez azt jelenti, hogy a T1 minden tevékenységét meg nem történtnek kell tekinteni. Így a w1(x) utasítást is el kell felejteni. A probléma viszont ebben gyökerezik, hiszen a w1(x) eredményét a T2 már felhasználta, s most kiderült, hogy téves adatokat használt T2. Emiatt T2 egész működése is téves alapokon nyugszik, ezért T2 eredményei is hamisaknak tekinthetők. Ebből viszont az következik, hogy T2 tranzakciót is vissza kell görgetni, hiszen téves, inkonzisztens adatok nem maradhatnak az adatbázisban. A T2 visszagörgetését viszont csak akkor lehetne elvégeznio, hanem felrúgnánk az ACID elvek tartóssági elvárását, mely kimondja, hogy véglegesített tranzakció hatása vissza nem törölhető. A T2 pedig már véglegesítésre került a Commit utasítás során. Az így kialakult helyzetet nem lehet megnyugtatóan feloldani, hiszen vagy az adatbázis kerül inkonzisztens állapotba, ha nem törlöm vissza T2-t, vagy az ACID elveket rúgjuk föl, ha kitöröljük T2-t. A megnyugtató megoldás csak abban kereshető, ha megpróbáljuk kizárni ilyen ellentmondásos helyzetek kialakulását. Vagyis a tranzakció kezelőnekk gondoskodnia kell az ilyen helyzeteket elkerüléséről. Hogy el tudjuk kerülni a fenti helyzetet, előbb ki kell deríteni, hogy a history gráf mely eleme vagy mely elemei okozzák azt ellentmondást. Ha újra végiggondoljuk a szituációt, akkor látható, hogy elkerülhető lenne az ellentmondás, ha T2 még nem véglegesítődött volna. Vagyis T2-nem szabad véglegesítődnie, mielőtt T1 is véglegesítődne. A két tranzakció között kanfliktus abból eredt, hogy T2 felhasználta azt az adatot, amit T1 írt. Belátható, hogyha nem lenne olyan objektum, amelyet mindkét tranzakció felhasználna, pontosabban ha az egyik tranzakció nem érintené a másik tranzakció által módosított adatokat, akkor lényegtelen lenne a lezárások sorrendje is. Az előbb elmondottakból tehát egy olyan megállapítás, szabály vonható le, hogy ha a history-ban egy T2 tranzakció olvas egy olyan objektumot, melyet közvetlen előtte egy másik T1 tranzakció írt, akkor T2 csak akkor záródhat le, ha már T1 lezáródott. A fenti szabálynak eleget tévő history-kat szokás RA-history-nak nevezni. Az RA jelölés a RecoverAble, vagyis visszagörgethető tulajdonságra utal. A kiinduló példánk egy javított változatát mutatja be a következő ábra: w1(x) -> r1(y) -> w1(y) -> a1 | | v v r2(x) -> w2(x) -> c2 A felírt gráfban már T1 lezárása után következik T2 lezárása. Második példánkhoz is ezen ábrából kiindulva, anak egy kissé módosított alakját használjuk fel. Az ábrában több tranzakciót is tartalmaz ahistory, melyek sorban egymásból olvasnak. w1(x) -> r1(y) -> w1(y) -> a1 | v r2(x) -> w2(x) -> ... | v r3(x) -> w3(x) -> | v r4(x) -> ... A felvázolt gráfrészletet gondolatban úgy bővítsük, hogy eleget tegyen az RA követelménynek. Az RA követelmények teljesítése miatt a T1 tranzakció ortálása ugyan nem okoz feloldhatatlan ellentmondást, viszont hatása semmiképpen sem nevezhető kellemesnek. A probléma abban rejlik, hogy a T1 abortálása után T2 sem fog konzisztens képet előállítani, hiszen rossz induló adatokat használt fel. Emiatt a T2 tranzakciót is újra kell kezdeni, hogy konzisztens adatokkal végezze el újra a műveleteit. A T2 is abortálódnia kell tehát. A gráfról látható azonban az is, hogy a T3 tranzakció viszont a T2 által kiírt adatokra épült, tehát ebből következően, ha T2-t vissza kell görgetni, akkor T3 induló adatai sem tekinthetők helyesnek, konzisztensnek, ezért a T3 tranzakciót is vissza kell görgetni. De mivel a T3 közbenső adatait a T4 már felhasználta, ezért a T4 sem maradhat meg, ezért őt is vissza kell görgetni. S a visszagörgetések sorozata így folyttaódhat tovább. A példában tehát egy visszagörgetési láncolat, lavina indult el egyetlen tranzakció abortálása miatt. Ezen abortálási lavina azonban igen károsan hat az adatbáziskezelő rendszer hatékonyságára. Az abortálás mindíg elfecsérelt erőforrást, energiát és időt jelent, s sokszor nemcsak a számítógép idejére vonatkozóan, hanem a felhasználók idejére is kihatóan. Emiatt a DBMS rendszerek jogos törekvése, hogy a konfliktusok miatti belső abortálásokat a minimális szinten tartsák. Az abortálási lavina megaakadályozására ismétcsak meg kell vizsgáálni, hogy mi volt a rossz a afelvázolt history gráfban, mi vezetett az abortálási lavinához. Mint látjuk a lavina oka az volt, T2 olyan adatokat olvasott, amelyek helyessége még nem dőlt el. A kiírt adatról csak a T1 lezárásakor dől el, hogy megmarad, vagy törlődik s a korábbi érték görgetődik vissza. Emiatt T2 vállalta azt a veszélyt, hogy később T1 miatt neki is abortálódnia kell. A DBMS viszont az erőforrás pazarlások megakadályozása végett nem engedhet meg ilyen kockázatokat, tehát T2 csak olyan adatokat olvashat, melyek helyessége már biztosított, vagyis csak a T1 által véglegesített adatokat szabad felhasználni. A levezetett szabály tehát azt mondja ki, hogy a history-ban egy T2 tranzakció csak olyan adatokat olvashat, melyet egy már lezárt tranzakció módosított utóljára. A fenti elveknek elget tévő history-t ACA history-nak nevezik, ahol az ACA rövidítés az Avoiding Cascadeless Abort, vagyis az abortálási lavina megelőzésére utal. Az induló példa egy javított változatát mutatjuk be következő ábrán, melyben minden tranzakció megvárja az előző lezárást, s csak utána olvassa az objektum értékét. w1(x) -> r1(y) -> w1(y) -> a1 | v r2(x) -> w2(x) -> ... | v r3(x) -> w3(x) -> A soronkövetkező esetben már mind az RA, mind a ACA követelmények teljesülnek, mégis akad probléma a végrehajtás során. r1(x) -> w1(x) w1(z) -> a1 | ^ | v | v r2(z) -> w2(x) a2 A példában egyik tranzakció sem olvassa a másik által módosított adatokat, mégis hibás lesz a history eredménye. A végrehajtás során ugyanis előbb a T1, majd a T2 módosítja az x objektumot, s a mindkét tranzakció abortálódik a végén. A probléma az abortálások miatt lép fel. Az abotálás során a rendszernek vissza kell állítani a módosítás előtti értékeket minden érintett objektumnál. Mivel előbb a T1 tranzakció abortálódott, ezért a rendszer előbb visszaírja a w1(x) előtti értéket az adatbázisba. Ezután a T2 is abortálódik, s ez alkalommal pedig a w2(x) előtti értéket kell visszaírni az x-be. De ahhoz, hogy visszaállítsuk az induló értéket, meg kell őrizni az induló értéket, vagyis ki kell olvasni a módosítás előtti értéket. Vagyis a tranzakció kezelő rendszernek minden írási művelet előtt egy olvasási műveletet is végre kell hajtania. A példánkban a w2(x) -höz tartozó olvasási művelet egy hibás, nem véglegesített értéket kapott, hiszen még futó tranzakció írta az x-et utóljára. A megoldás kulcsa most tehát abban rejlik, hogy az előbbi pontban megfogalmazott megkötést kiterjesszük az írási műveletekre is, hiszen az írási műveletek is tartalmaznak implicit olvasási lépéseket is. A megfogalmazott tmegkötés tehát úgy szól, hogy a history-ban egy T2 tranzakció csak olyan adatokat olvashat vagy írhat, melyet egy lezárt tranzakció módosított utóljára. A fenti követelményelknek eleget tévő history-kat szokás ST, vagyis szigorú (strict) history-nak nevezni. E követelményt kielégítő módosítást mutat be az alábbi ábra. r1(x) -> w1(x) -> w1(z) -> a1 | v r2(z) -> w2(x) -> a2 A megfelelő history-k kialakításhoz szükséges feltételek bemutatásához még egy példát veszünk, amely szintén egy igen fontos hibalehetőségre hívja fel a figyelmet. A minta gráf a következő alakú: r1(x) -> w1(x) -> c1 ^ | | v w3(x) -> c3 -> r2(x) w2(x) -> c2 A gráf megfelel az előzőekben definiált feltételeknek, hiszen egyik tranzakció sem olvas a másik tranzakció által írt adatokat. Ennek ellenére igen meglepő eredményt tapasztalunk, ha lefuttajuk a history-t. Tegyük fel, hogy a T1 tranzakció a normál fizetéseket viszi fel a dolgozók banki számla nyilvántartására. Eközben a T2 tranzakció a bérügy egy másik részlegéről indítva a rendkívüli jutalmakat osztja ki. Az x objektum jelölje egy dolgozó számlaállását, melynek induló értéke legyen pl. 22222. A példánknak megfelelően előbb T2 kiolvassa az aktuális állást, majd T1 is kiolvassa ezt az értéket. Mindketten ugyanazt azt a 22222 értéket kapják. Ezután T1 megnöveli a 22222-t a fizetés értékével, mondjuk 40000 -rel, majd kiírj az új értéket, a 62222-t az adatbázisba. Ezután a T2 végzi el a számlanövelést, ahol a rendkívüli jutalom értéke 20000. A T2 tranzakcióban a számlaállás új értéke 42222, s ez az új érték kerül kiírásra az adatbázisba. A kiírás után, illetve a T2 lezárása után, az adatbázisban ezen érték, vagyis a 42222 foglal helyet. Így a két tranzakció együttes hatásaként 42222 lett a dolgozó számlaállása. Ez az érték viszont nem az amit a dolgozó jogosan elvárna. Hiszen ő a 82222 értékre számit, mivel kapott rendes fizetést és rendkivüli jutalmat is. A history végrehajtásakor az egyik pénzösszeg eltűnt. A fentvázolt jelenséget, vagyis amikor a tranzakciók úgy hajtódnak végre, hogy az egyik tranzakció által elvégzett módosítást a másik tranzakció felülírja, eltünteti, lost update, vagyis az elveszett módosítás jelenségének mondjuk. Az értékmódosítás elvesztésének oka az, hogy mindkét tranzakció ugyanazon az induló x értékkel dolgozott, s erre építve módosította később az adatelemet. Ha tehát arra keressük a választ, hogy hogyan lehetne elkerülni e hibát, akkor azt látjuk, hogy meg kellene tiltani, hogy olyan értéket olvassunk ki, melyet más tranzakció is fel fog használni. Ha mélyebben belegondolunk, látjuk, hogy valójában itt sem az olvasással van a gond önmagában, hiszen, ha vagy a T1 vagy a T2 nem módosítaná x értékét, akkor nem következne be a hiba sem. A gondot megint csak a konfliktusban álló műveletek környékén kell keresni. A minta gráfban a T1 beli olvasás megelőzi a T2 beli írást, míg a T1 beli írás követi a T2 beli olvasást. Vagyis a két tranzakció konfliktusan álló műveletei keresztbe-kasul követik egymást, s ez okozza a hibát. Ennek felismerése után adódik a megelőzés receptje is. Egy history-ban két tranzakció között a konfliktusban álló műveletek között azonyos irányú megelőzési relációnak kell lenni, vagyis ha az egyik konfliktusban álló műveletpárnál T1 művelete következik előbb, akkor bármely más idetartozó műveletpárnál T1-beli műveletnek kell következni előbb. A fenti kritériumnak eléget tévő history-t SR, sorossal ekvivalens (serializable) history-nak mondjuk. A megjelölésben a sorosság arra utal, hogy a műveletek a tranzakciók szerinti sorrendben hajtódnak végre. A tiszta, teljes soros végrehajtásnál minden műveletre érényes, hogy a különböző tranzakciókhoz tartozó műveletek időben egymással nem keveredhetnek. Az ilyen végrehajtáshoz tartozó history-kat S, azaz soros (serial) history-knak nevezik. A következő ábra egy ilyen S history-t mutat be. r1() -> w1() -> ... -> c1 -> w2() -> ...-> c2 -> r3() * ... T1 -> T2 -> T3 A feltételünkben azonban mi kell ilyen szigorú sorrendiséget megkívánnunk, hiszen mint láttuk, hogy csak a konfliktusban álló elemek között lényeges a soros végrehajtás. Mind már korábban láttuk, hogy a history-k ekvibvalenciájánál is csak a konfliktusban álló elemek játsznak szerepet, s ezekre szorítkozva pedig soros a végrehajtás, ezért mi nem a teljes sorosságot, hanem csak a sorossal ekvivalens végrehajtást követeljük meg. A példákban egyre újabb megkötéséket vettünk az ACID elveknek megfelelő history-k kialakításához. Az egyes megkötések egymáshoz való viszonyát vizsgálva látható, hogy ezek egyre szűkülő feltételeket adnak. Így például egy ACA history egyben RA history is. Hiszen ha T2 olvas T1-ből, s az olvasáskor már T1 lezárt, sT2 lezárása is még később lesz csak, tehát T2 lezárása szükségszerűen később van, mint T1 lezárása. Az ST és ACA history esetén is látható, hogy az ST teljesítése egyúttal ACA teljesítését is jelenti, hiszen az ST definíciója megában foglalja az olvasás műveletét is, így magával hozza az ACA teljesülését is. Az S history-ról is könnyen belátható, hogy a soross végrehajtás esetén, ha T2 olvas, vagy ír egy olyan adatelemet, melyet T1 módosított utóljára, akkor T2 minden művelete követi T1 lezárását, hiszen az S history-k esetén a különböző tranzakciókhoz tartozó műveletek nem keveredhetnek. A SR history-k esete azonban már nem annyira illik be ebbe gondolatmenetbe. Ennek belátására vegyük az alábbi minta history-t. r1(x) -> w1(x) a1 | ^ v | r2(x) -> w2(x) -> c2 A history teljesíti az SR feltételt, hiszen minden konfliktusban álló műveletpár esetén a T1 megelőzi a T2 tranzakciót. Azonban az is látható, hogy a fenti history gráf még az RC feltételnek sem tesz eleget, hiszen T2 azelőtt véglegesítődik, mielőtt a T1, amiből olvasott, lezáródna. Tehát nem minden SR history teljesíti az RA feltételt, így az SR history-k halmaza nem tekinthető az ST historyk részhalmazának. A különböző history-k egymáshoz való viszonyát szemlélteti a következő ábra is. Az ábrából is látható, hogy az ACID elvek betartása csak olyan history-val lehetséges, amely egyrészt teljesíti az ST elveket, másrészt az SR feltételeknek is megfelel. Az ábrára utalva, tehát a tranzakció kezelő rendszernek csak olyan history-kat szabad megengednie, melyek az SR és az ST halmazok metszetébe beleesnek. Most már tehát látható, hogy a tranzakció kezelő rendszerek célja a fenti SR-ST history-k biztosítása. A célok kijelölése után nézzük, hogy milyen módszerek terjedtek el e célok megvalósítására. Előbb vesszük az ismertebb zárolási módszerek, majd bemutatjuk a különböző időpecsét, és SG gráf alapú eljárásokat is. A módszerek ismeretése után kitérünk az Oracle rendszerben megvalósított mechanizmusra is. Zárolási módszerek A zárolás az egyik legismertebb és leginkább elterjedt tranzakció kezelési módszer. A zárolás alapelve, hogy a tranzakció rátelepszik, lefoglalja az objektumot arra az időre, amíg használni szeretné. E zárolás hatására a többi tranzakció korlátozva van az objektum elérésében, ezért rendszerint várakozásra kényszerülnek. A várakozás akkor vagy oldódik fel, ha újra elérhető már az objektum, vagy ha kiderül, hogy reménytelen a várakozás, nem érdemes már tovább várakozni. A zárolás során minden objektumál nyilván kell tartani, hogy szabad-e vagy sem, s ha nem ki foglalja. Erre azért van szükség, hogy az igény elmultával tudjuk, hogy ki szabadíthatja fel ismét az objektumot. Emiatt tehát az objektumokat el kell látni kiegészítő információkkal, melyek a zárolás állapotára vonatkoznak. Mivel ez kiegészítő információk tárolását igényli, ezért a zárolási mechanizmus helytöbblettel jár. A helytakarékosság szempontjából tehát nem lényegtelen, hogy mely és mennyi objektumot lehet zárolni. Az egyes tranzakció kezelő (TM) rendszerek különbözhetnek egymástól abban, hogy mit tekintek zárolási egységnek. Mivel az adatbázisokban tárolt adatoknak is több szintje van, ezért a zárolási szintek bevezetése is igen természetes megoldásnak tűnik. Ennek megfelelően a zárolási szintek az adatbázis objektumok szinteit tükrözik vissza. A zárolási objektumszint a zárolás finomságát határozza meg. A finomság legfinomabb szintje a mezők szintjének felel meg. Mivel Codd értelmezése alapján a relációs adatmodellben a mező a legkisebbb adatbázis objektum, ezért ennél finomabb zárolási egységre elviekben sincs lehetőség az RDBMS-ek esetében. A következő szintet a mezőkből felépülő rekordok, tábla sorok jelentik. Ekkor egy teljes sort lehet egyszerre lefoglalni, majd felengedni. A legnagyobb lefoglalási egységet a táblák szokták jelenteni, mely során a tábla minden rekordja zárolásra kerül. Mint elvárható, mind a finom, mind a durva zárolási szintnek vannak előnyei és hátrányai. A finom zárolási, vagyis a mező szintű zárolások előnye, hogy nagyobb fokú párhuzamosságot, konkurenciát tesz lehetővé. Hátránya viszont, hogy sokkal több kiegészítő információt kell tárolni és karbantartani, ami mind jelentős hely, mind idő költségnövekedést jelent. A durva, vagyis tábla szintű zárolás előnye ebből következően, hogy sokkal egyszerűbb nyilvántartani és sokkal gyorsabban is adminisztrálható. Hátránya viszont, hogy nagyon lecsökkenti a konkurrenciát, nagyon sokáig kell várakozni a párhuzamosan futó tranzakcióknak egymásra. Mivel a tranzakciók közötti konkurencia mértéke a párhuzamos hozzáférések valószínűségétől függ, ezért a zárolási költség a zárolás finomságának függvényében különböző alakot vesz fel az eltérő párhuzamos hozzáférési valószínűségek esetén. E költségfüggvény rendszerint nem az egyes szélső, nagyon durva, vagy nagyon finom helyeken lesz minimálsi, hanem a középső, tehát a rekord szintű zárolások közelében. A következő ábra bemutatja e költségfüggvényt mind a hosszú, mind a rövid tranzakciók esetén. Mint látható a hosszú tranzakciók inkább a finomabb, a rövidebbek pedig a durvább zárolási szinteket favorizálják. Ennek az a magyarázata, hogy a hosszabb tranzakciók esetén megnövekszik a konkurencia valószínűsége, ezáltal a durvább zzárolási szint okozta várakoztatási költség jelentősebb szerepet játszik. A rövidebb tranzakciók esetén viszont kicsi a konkurencia esélye, ezért a várakzotatás költsége is alacsonyabb, így relatívan nagyobb szerephet jut a zárolás adminisztrációs költsége, amely viszont a durvább zárolási szinteket kedveli. A záorlási szint kiválasztása után a következő kérdés az, hogy mikor és meddig zároljunk egy objektumot. Alapelvként elfogadthatjuk,hogy csak addig zároljunk egy objektumot amíg feltétlenül szükséges. Ebből eredően akkor kell lefoglalni egy objektumot, amikor szükség van rá. Amig a tranzakció nem ér ahhoz a ponthoz, amikor kezeli az objektumot, addig hagyja, hogy más tranzakciók dolgozzanak az objektummal. A zárolás felengedésének időpontja is könnyen kikövetkeztethető. Ha ugyanis a zárolást akkor engednénk fel, amikor a tranzakciónak nincs már szüksége az objektumra, netán rögtön a kezelő művelet után azonnal felengednénk a zárolást, akkor egy másik tranzakció lehetősget kapna, hogy elolvassa a felengedett objektumot, s esetleg felhasználja a kapott értéket. Így T2 olyan értéket használhatna, melyet T1 írt utóljára úgy hogy T1 még nem került véglegesítésre. Ezáltal viszont megsérülne mind az RA, ACA feltétel. Tehát a zárolást csak akkor szabad felengedni, ha már véglegesnek tekinthetők a benne tárolt adatok. Így a zárolás felengedésének időpontja a tranzakció vége. A zárolás ezen mechanizmusát kétfázisú zárolásnak (2PL:. 2 phase locking) nevezik, utalva arra hogy előbb a zárolások lefoglalása történik (ez az első fázis), majd eztkövetően, a tranzakció végén felngednek minden lefoglalt zárolást. Így előbb csak gyülnek a zárolások, majd egyszerre felengedésre kerülnek. E folyamatot szemlélteti a következő ábra is. A zároláés első fázisa valójában szinte az egész tranzakcióra kiterjed, míg a második fázik a tranzakció lezárásához kapcsódik csak. A 2PL zárolási mechanizmus biztosítja a legtöbb RDBSM rendszerben az ACID elveknek megfelelő history-k megvalósítását. A másik ide kapcsolódó kérdés, hogy mely műveleteknél zároljuk az objektumot. Az ST és az SR history-k szabályai szerint a konkurrens műveletknek sorosan kell végrehajtódniuk, beleértve a tranzakciók lezárási utasításait is. Ha például egy írási művelet után nem zárjuk le az objektumot, akkor rögtön olvashatja a módosított érétéket gey másik tranzakció, mielőtt az új érték véglegesítődne. Így a history nem teljesíti már az RA követelményeket sem. Tehát egy írási műveletkor szükséges a zárolás. Ha egy olvasási műveletet veszünk, és nem zároljuk az objektumot, akkor egy másik tranzakció szintén olvashat belőle, majd egymásután módosíthatják az objektum értéket a beolvasott érték alapján, mely során az egyik módosítás felülírhatja a másikat, vagyis bekövetkezhet a lost update jelensége. Az előzőekből következően mind az olvasás, mind az írás műveletekor le kell foglalni az objektumot. A gyakorlati rendszerek azonban egy kissé módosítottan alkalmazzák ezt a zárolási előírást. Ennek oka, hogy a mondottak alapján két olvasási művelet sohasem áll konfliktusban egymással, tehát tetszőleges sorendben lehet őket végrehajtani. A egyszerű zárolás általunk említett változatában viszont az olvasás épúgy lefoglalja az objektumot, mind az írás művelete. A zárolás alatt pedig más tranzakció nem olvashatja és nem írhatja a foglalt elemet. Vagyis a felvázolt modell túlságosan szigorúan kezeli a zárolásokat, ami jelentősen lecsökkentheti a TM által biztosított konkurencia szintet.. Az egyes TM módszerek más-más utakat keresnek e túlzott szigor enyhítésére. Bizonyos rendszerekben kétfajta zárolási tipust vezetnek be, s van egy olvasási, s van egy írási zárolás. Ekkor az irási zárolás azt jelenti, hogy egy irási műveletet hajtott végre az aktuálsi tranzakció az objektumon. Az irási zárolás egy szigorú megkötést jelent, s az írási zárolás alatt más tranzakció se nem olvashatja, se nem módosíthatja az objektum értékét. Az olvasási zárolás, ami akkor következik vbe, ha egy olvasási műveletet hajtottak végre az objektumon, csak azt akadályozza meg, hogy más tranzakció módosítsa az olvasott értéket. Az objektum olvasását nem akadályozza ezen zárolás. E eljárás ugyan enyhíti a konkurenciára vonatkozó megszorításokat, azonban biztosítja a lost update-hez kapcsolódó problémát. A bemutatáshoz vegyük újra a minta history gráfot. r1(x) -> w1(x) -> c1 ^ | | v w3(x) -> c3 -> r2(x) w2(x) -> c2 A normál zárolás esetén előbb T2 lefoglalja az x objektumot. Az ezt követő r1(x) már nem tud végrehajtódni, hiszen az x már foglalt. Az r1(x) várakozásra kényszerül mindaddig, amíg a T2 be nem fejezi x módosítását és le nem záródik. T2 lezárása után folytathatja munkáját T1, amely elsőként beolvassa x aktuális értékéyt (amely a T2 által megnövelt értéklesz), majd ezt módosítja a saját algoritmusa szerint. A felvázolt history végrehajtás tehát elkerülte a lost update jelenséget. Ha azonban a rendszerbe minden objektumhoz egy olvasási és egy írási zárolás jelzőt vezetünk be, akkor már nem oldható meg a lost update problémája ezúton. Ebben a rendszerben ugyanis előbb T1 zárolja x-et olvasási zárolásra. Az olvasási zárolás jellemzője alapján T2 is olvashatja x-et. Ekkor megváltozik az z elem olvasási foglaltság jelzője T2 értékre. Emiatt w2(x) elvégezhető. A w2(x) végrehajtása már ugyan írásra zárolja az elemet, ami megvárakoztathatja w1(x) -et, azonban T2 lezárása után T1 csak kiírhatja az induló x érték alapján kiszámított módosított értéket. E history lejátszása után a T2 által elvégzett módosítások ismét elvésznek. Ez a módszer tehát nem biztosít olyan tökéletes megoldást, mint az előzőleg vett zárolási mechanizmus. A zárolási mechanizmus tökéletesítésére vannak olyan megoldások, melyek egy írási zárolási és több olvasási zárolást tartanak nyilván. Ebben a konstrukcióban minden olvasás létrehoz egy saját olvasási zárolást. Bármely olvasási zárolás csak a saját tranzakciónak enged írási műveletet. Így ha több olvasási zárolás van, egyetlenegy tranzakció sem végezhet írási műveletet. A példánkban a zárolások most a következőképpen alakulnak. Előbb a T2 zárolja olvasásra az x objektumot. Ezután jön r1(x), amely most végrehajtható, de hatására a T1 is zárolja olvasásra az x objektumot. Most következne a w1(x) írási utasítás, melyet viszont a TM nem enged azonnal végrehajtani, hiszen az x objektumon T2 olvasási zárolás van. Tehát a T1 várakozásra kényszerül. Közben T2 is továbbfut és elérkezik a w2(x) műveletehez. A TM azonban ezt sem engedi lefuttani, hiszen a x objektumon még ott van T1 olvasási zárolása. Emiatt a T2 is várakozásra kényszerül. Ezzel azonban egy új helyzet állt elő a history végrehajtás során. Igaz ugyan, hogy sikerült elkerülni a lost update jelenségét, de van két olyan tranzakció a history- ban, melyek egymásra várnak, s egyik sem tud továbblépni. Ez az úgynevezett deadlock jelenség. A deadlock a zárolási mechanizmusok egyik kellemetlen velejárója . A dedalock akkor lép fel, ha több tranzakció egymásra várakozik, s egyik sem tud lépni. E keresztbe várakozásra mutat be példát a következő ábra, melyben T1 foglalja x-et, s várakozik y-ra.. Az y-t azonban T2 foglalja, amely viszont x-et szeretné elérni a következő műveletben. Mivel egyik tranzakció sem tud továbblépni, mindkettő végtelenségig várakozna. A TM rendszer egyik feladat, hogy felismerje, s feloldja a dead lock jelenségeket. A feloldásra két alapmódszer létezik: - timeout módszer - WFG gráf A timeout mechanizmus, melyben a rendszer figyeli, hogy mennyi ideig várakoztt a tranzakció a zárolás feloldására. Ha a várakozási idő túllép egy megadott időhatárt, akkor a kialalkult helyzetet a TM egy deadlock jelenségként értékeli, melyet fel kell neki oldania. A deadlock feloldására alkalmazható módszer az egyik tranzakció abortálása. Az visszagörgetendő tranzakció kiválasztásának problémája megint csak egy önmagában érdekes probléma, hiszen számos eltérő stratégia létezik a megoldására. A helyzet sokban hasonlít az OS rendszerek memóriakezelési feladatára, amikoris a foglalt lapokból kellet egyet kiválasztani a memóriából való kimozdításra.. Itt lehet választani, hogy mit preferálunk az egyes tranzakciók értékelésénél, pl. az eltöltött időt, az elvégzett módosításokat, a még hátralévő időt, stb. A WFG gráf módszernél ismét a gráfok hatékony formalizmusát használják fel a dedalock észlelésére. A WFG, vagyis várakozási gráf (wait-for graph) elemi a futó tranzakciók. A gráf élei irányítottak, s akkor tartalmaz a gráf élet a T1 csomópontból a T2 csomópontba, ha van olyan objektum, melyet T2 lefoglalt, s melyet T1 is szeretne elérni, s T1 most ezen objektum felszabadaulására vár. Vagyis T1 arra vár, hogy T2 elengedje a zárolt objektumot. A deadlock azt jelenti a várakoztatások szintjén, hogy nincs vége a várakoztatási láncnak, vagyis nincs esély, hogy a várakoztatási lánc valamikor is megszünjön. Ez a WFG formalizmussal azt jelenti, hogy akkor lép fel a deadlock jelensége, ha a WFG-ben létezik egy körút. Ha a WFG körútmentes, akkor a history végrehajtás során nem lépett fel deadlock. Az SGT módszer A history SR tulajdonságának ellenőrzésére szolgál az SGT (serialization graph testing) módszer, amely lehetővé teszi, hogy viszonylag gyorsan és áttekinthető módon ellenőrizzük, hogy a konfliktusban álló műveletekre vonatkozóan teljesül-e a tranzakciók soros végrehajtása. A sorossal ekvivalens végrehajtás esetén bármely két tranzakció között csak egyféle megelőzési reláció érvényesül. Az SG gráf a history-khoz rendelhető, s a kapcsolódó history SR voltának eldöntésére szolgál. Az SG gráf, egy olyan gráfot jelöl, melynek elemei a history-ban futó, commit-tal véglegesített tranzakciók, s élei irányítottak. Akkor létezik él egy T1 csomópontból a T2 csomópontba mutatva, ha létezik olyan konfliktusbn álló műveletpár, melynek egyik tagja T1-hez, a másik tagja T2- höz tartozik, s a T1 csomóponthoz tartozó művelet megelőzi a T2-höz tartozó műveletet. A definícióból következően az SG gráfban két csomópont között két él is létezhet, melyek ellentétes irányításúak. Az egyik T1-ből mutat T2-be, a másik T2-ből mutat T1-be. Az SG gráfban a definícióból következően nem vesznek részt az abort-tal visszagörgetett tranzakciók. Az SG gráf a tranzakciók konfliktusban álló műveleteire vonatkozó sorrendiségét emeli ki. Az SR history esetén a history linerizálható kapcsolatban álló tranzakciókból épül fel, vagyis nem keverednek a különböző tranzakciókhoz tartozó műveletek, a konfliktusban álló műveletekre szorítkozva. A SR tulajdonság az SG gráf segítségével a következő tétel alapján ellenőrizhető. A history akkor és csak akkor SR tulajdonságú, ha a hozzá tartozó SG gráf körútmentes. A tétel azon alapul, hogy körút esetén a tranzakciók már nem alkotnak lineáris láncot, vagyis a konfliktusban álló, különböző tranzakcióhoz tartozó műveletek között keveredés van. Példaként vegyük az alábbi history gráfot, s határozzuk meg az SG gráfját, s döntsük el, hogy SR history-t ábrázol-e. A minta history a következőképpen népül fel: r1(x) -> w1(y) w1(y) -> c1 ^ | ^ | v | r2(z) -> w2(z) r2(y) -> w2(z) -> c2 ^ | r3(x) -> w3(x) -> c3 ^ | r4(z) -> w4(y) -> a4 A history-hoz tartozó SG gráf már csak három csomópontot, a T1, T2 és T3 tranzakciókat tartalmazza. A T1-ből T2-be vezető élet a w1(y) és r2(y) műveletek miatt kell megadni, míg a T2-ből T1-be vezető út az r2(y) és w1(y) miatt szerepel az SG gráfban. A felvázolt SG gráfot ellenőrizve könnyen felfedezhető egy körút, mégpedig a T1 * T2 * T1 nyomon haladó körút. Mivel az SG gráf nem körútmentes, ezért a hozzá tartozó history sem SR history. Az SG gráf módszeren alapuló tranzakció kezelés egy kicsit módosítja az alkalmazott SG gráfot a gyakorlati igényeknek megfelelően. A módosítás két lényegi elemet tartalmaz. - A régebbi, érdektelen tranzakciók kikerülnek az SG gráfból. Ennek elsősorban helytakarékossági okai vannak, hiszen minden bennlévő tranzakcióról nagy mennyiségű adatot kell nyilvántartani. Amiatt igényel egy csomópont sok helyet, mert az élek kialakításához szükséges ismerni a tranzakció által elvégzett műveleteket. Így minden tranzakcióhoz nyilván kell tartani, hogy mely objektumokat olvasta illetve írta. Ez a hosszabb tranzakciók esetén jelentős helyfoglalással jár, ezért célszerű a már érdektelen tranzakcióktól megszabadulni. - A felhasznált SG gráf tartalmaz még le nem zárt, hanem mmég futó tranzakciókat is. Ennek oka, hogy időben felfedezzék a fellépő ciklikusságot, s ne csak a tranzakció végét jelentő c vagy a műveletnél derüljön ki, hogy vissza kell görgetni a teljes tranzakciót egy sokkal korábi konfliktus miatt. Ha időben felfedezzük a körutat, akkor kevesebb idő és erőforrás pazarolódik el a abortálandó tranzakcióknál. Az SG gráfon alapuló tranzakció kezelés alapja a következő mechanizmus: - Egy újabb beérkező művelet esetén a tranzakció kezelő (TM) ellenőrzi, hogy benn van-e már a tanzakció az SG gráfban vagy egy új tranzakcióról van szó. Ha új tranzakció jött be, akkor felkerül az SG gráfba, mint új csomópont. - Ezután ellenőrzésre kerül, hogy a művelet mely más korábban kiadott művelettekkel van konfliktusban, s minden érintett csomóponthoz él kerül fel az aktuális tranzakció csomópontból kiindulva. - Az új SG gráfot tesztelik, hogy tartalmaz-e körutat. - Ha igen, akkor, az új művelet nem érvényesíthető, így a tranzakcióját is vissza kell görgetni. Ehhez a tranzakció kap egy abortálási parancsot, majd a visszagörgetés után az SG gráfból is törkődik a tranzakciót reprezentáló csomópont a hozzá tartozó élekkel együtt. - Ha viszont nem tartalmaz körutat az SG gráf, akkor művelet engedélyezhető. Ehhez előbb megvárja a korábbi műveletek siekres nyugtázását, tehát hogy azok valóban sikeresen befejeződtek, majd ezután engedi végrehajtani a bejött műveletet. A SG gráfon alapuló TM rendszerek egyik érdekes kérdése, hogy mikor lehet törölni egy csomópontot. Egy csomópont csak akkor törölhető, ha a jövőben már biztosan nem válhat egy körút láncszemévé. Mivel egy csomópontba bármikor mutathat új él, hiszen jöhetnek olyan későbbi műveletek, melyek konfliktusban állnak a csomópont műveletivel, ezért a körútban való részvétel csak akkor tekinthető kizártnak, ha a csomópontból nem indul és nem is indulhat ki új él. Egy új él akkor indul ki a csomópontból, ha egy művelete megelőz egy másik vele konfliktusban álló műveletet. A tranzakció lezárása után ilyen művelet már nem jöhet létre, ezért ha a tranzakció lezárásakor nem indul ki él a tranzakcióból, akkor a jövőben som fog kiindulni. Tehát egy csomópontot akkor lehet törölni a hozzá tartozó élekkel együtt, ha már lezáródott és nem indul ki belőle él. Mivel ekkor számos él is megszűnhet, létezhetnek olyan csomópontok,melyek ez által válnak törölhetővé. Így egyetlen csomópont törlése több más csomópont törlését is kiválhatja. Timestamp ordering Az időpecsét sorrenden (TO, timestamp ordering) alapuló ütezemés alapelve az, hogy minden tranzakcióhoz rendel egy születési dátumot, vagy időpecsétet, amely jelzi, hogy mikor is jött létre a tranzakció a többi tranzakcióhoz viszonyítva. Az időpecsét tehát egy sorszámnak is tekinthető, amley megadja, hogy hol helyezkedik el a tranzakció a többi tranzakcióhoz viszonyítva a létrehozási időpont tekintetében, így a orábban létrejött tranzakciók időpecsétje kisebb, míg a később létrejövő tranzakciók időpecsétje pedig nagyobb. A TM alapfilozófiája ennél a módszernél igen világos és egyértelmű: A különböző tranzakciókhoz tartozó, egymással konfliktusban álló műveletek esetén a műveletek végrehajtási sorrendje feleljen meg a tranzakciók időpecsét sorrendjének. Vagyis azon műveleteknek kell előbb végrehajtódniuk, amelyek időpecsétje kisebb. Ha a TM a végrehajtás során olyan kisérletet tapasztal, hogy egy tranzakció olyan objektumot akar módosítani, vagy olvasni, melyet egy fiatalabb tranzakció már egyszer módosított, akkor ezen műveletet megakadályozza, méghozzá úgy, hogy ezen idősebb tranzakciót aborátlja. A visszagörgetés után pedig újra indítja a tranzakciót, amley most már egy nagyobb időpecséttel újra próbálkozhat a műveletek végrehajtásával. A zárolás és időpecsét alapú módszerek egyik alapvető különbsége, hogy a zárolásnál a lefoglalás felengedése után bármely tranzakció hozzáférhet az objektumhoz, addig az időpecsét esetén nincs ilyen foglalási idő, az objektum bármikor elérhető, viszont csak azon tranzakciók férhetnek hozzá az objektumhoz, melyek fiatalabbak, mint a utólsó foglalást végző tranzakció. A zárolás hátránya, hogy sokat várakoztat, s deadlock léphet fel a tranzakciók között, az időpecsét módszer hátránya pedig az, hogy túlságosan könnyen kerülhet abortálásra egy tranzakció. Ugyan az alap TO mechanizmusban valóban nincs semmiféle várakoztatás, de ez mint könnyen belátható súlyos hibákhoz vezethet. A TO mechanizmus ugyanis nem azt figyeli, hogy az objektum feldolgozás alatt áll-e vagy sem, hanem csak az utólsó hozzáférést végző tranzakció időpecsétét figyeli. Mivel egy objektumon egyidűleg csak egy művelet végezhető, ezért a TO mechanizmusban be kell hozni egy olyan rendszer komponenst is, amely gondoskodik arról, hogy egy művelet ne kezdödjön el addig, amíg a korábban engedélyezett kisebb sorszámú műveletek be nem fejeződnek. Tehát szükség van a műveletek késleltetésére ezen mechanizmusban is. A műveleti szintű késlelteések mellett azonban szükségesnek bizonyul egy nagyobb méretű késleltetésre is, mint az az alábbi példa is mutatni fogja. A vizsgált history gráf felépítése a következő. w1(x) -> r1(y) -> w1(y) -> a1 | ^ v | r2(x) -> w2(x) -> c2 A history talán ismerősnek tűnik, hiszen az RA history tárgyalásakor ugyanilyen szerkezettel találkoztunk. A TO ütemezés esetén az x objektumot előbb T1 majd ezután T2 kezeli. Ez egy megengedett sorrend. Az y objektumhoz pedig csak a T1 fér hozzá, ami szintén engedélyezett. Így mindkét tranzakció lefuthat a sorrend bárminemű változtatása nélkül, és ez problémát fog okozni, hiszen a T1 abortálása miatt r2(x) hamis, inkonzisztens értéket kapott, így T2-nek is vissza kellene görgetődnie, amit a kiadott és elfogadott commit utasítás miatt már nem lehet megtenni. Tehát a alap TO ütemzés önmagában nem biztosítja RA history végrehajtást. Emiatt a gyakorlatban az alap TO módszer egy módosított változatát alkalmazzák, amely megoldja a fenti problémát, nevezetesen azt, hogy minden tranzakció csak már véglegesített adatokhoz férjen hozzá. Ennek mechanizmusa megint csak a késleltetés lesz. Az úgynevezett ST-TO módszerben, amely olyan TO módszer, mely biztosítja a TO history-t, egy művelet addig kell késleltetni, amíg az objektumot utóljára módosító kisebb sorszámú tranzakció le nem záródik. Vagyis az ST-TO mechanizmusban is kénytelenek vagyunk egyfajta zárolást bevinni a tranzakció kezelésbe. A TO ütemezés esetén a működés alapeleme, hogy tudjuk, mely tranzakció használta az egyes objektumokat utóljára. Ehhez nyilván kell tartani az utólső hozzáféréseket. Így minden objektumhoz kell egy jelzőt társítani, amely tartlmazza az objektumot utóljára kezelő tranzakció időpecsétjét. A TM ezen jelző és az új igénnyel fellépő tranzakció időpecsétjének összehasonlítása alapján dönti el, hogy engedélyezi-e az objektumhoz való hozzáférést, vagy abortálnia kell az igénylő objektumot. Mivel egy olvasási művelet csak egy írási művelttel áll konfliktusban, ezért külön nyilván kell tartani mind az olvasásra, mind az írásra, hogy mely időpecsétű tranzakció volt az utólsó engedélyezett művelet igénylője. Így az objektumokhoz egy olvasási és egy írási jelzőt is csatolni kell az adatbázisban. Ez jelzőknek a TO szabályainak megfelelően egyre növekvő értékeket kell tartalmazniuk, a jelzők értéke sohasem csökkenhet. A TO mechanizmus által kikényszerített abortálások darabszámánakj csökkentésére számos elgondolás született. A kidolgozott javaslatokat tanulmányozva azonban rájöhetünk, hogy a tranzakció visszagörgetések darabszámának csökkentése csak a tranzakciók konkurenciájának a csökkentésével oldható meg. A meglévő javaslatok közül egyet most röviden be is mutatunk, amely a legbiztosabb megoldásra a teljesen soros végrehajtásra irányul. A javaslat alapelve, hogy a TM létrehoz egy várakozósort, amelybe a végrehajtásra jelentkező műveletek foglalnak helyet. A módszeralapelve, hogy a várakozólistában minden futó tranzakciónak legyen poziciója, bejegyzése és a műveletek végrehajtása során a bejegyzett műveletek közül mindíg a legkisebb sorszámú műveletet veszi sorra. Az alapelvek betartásával biztosítható a history soros végrehajtása, hiszen egy T tranzakció első művelete csak akkor kerülhet végrehajtásra, ha a rendszerben már nem létezik tőle kisebb időpecsétel rendelkező és még futó tranzakció. Ha viszont a tranzakció megkapta a vezérlést, akkor nem engedi el, míg ő maga be nem fejeződik, hiszen menetközben nem jöhet tőle kisebb sorszámú tranzakció. Ez pedig abból következik, hogy a később keletkező tranzakciók mindíg egyre növekvő időpecsétet, azaz sorszámot kapnak. A következő példa a lost update elkerülését mutatja be a soros TO ütemezés alkalmazásával. Elsőként álljon itt a history gráf. r1(x) -> w1(x) -> c1 ^ | | v c3 -> r2(x) w2(x) -> c2 A várakozósor tartalmát a következő lista szemlélteti, ahol a sorszámozás az időbeliséget jelöli, azaz az 1. sor az induló listatratlmat, míg az 5. sor a záró listatartalmat mutatja. 1: T1:r1(x), T2:r2(x) 2: T1:w1(x), T2:r2(x) 3: T1:c1, T2:r2(x) 4: T1: - T2:w2(x) 5: T1: - T2:c2 A TO ütemezések közül még egyet bemutatuk, amely az optimista TO ütemezési elnevzést kapta. Az elnevezés abból ered, hogy a TM megpróbálja kiküszöbölni a menetközbeni várakozásokat és később feleslegesnek bizonyuló visszagörgetéséket. A módszer alapelve, hogy a műveletek hagyjuk addig futni, amíg van esély a tranzakció megmaradására, s toljuk ki az ellenőrzéseket addig az időpontig, amíg az még lehetséges. Vagyis menetközben feltesszük, hogy a tranzakció sikeresen le fog futni, azaz optimisták vagyunk a tranzakció sikerességét illetően. Természtesen ezen elv érvényesítéséhez számos ponton módosítani kell az alap TO ütemzés mechanizmusán, struktúráján. A változások, a működés fontosabb elemei a következőkben foglalhatók össze. Az adatbázisban minden objektumhoz hozzá van rendelve egy írási időpecsétjelző és egy olvasási időpecsét jelző. Az jelzők az adott műveletet elvégző tranzakciók időpecsétjei közül a maximális értékű időpecsétet tartalmazzák. A rendszer sajátossága, hogy az egyes tranzakciók nem közvetlenül az adatbázist módosítják, hanem kapcsolódik hozzájuk egy egyedi munkaterület, ahová feljegyzik az elvégzett módosításaikat. Az így letárolt módosítások egyszerre, a tranzakció végén kerülnek csak át az adatbázisba. Ezáltal elérhető, hogy az adatbázis csak lezárt, véglegesített tranzakciók által módosított adatokat tartalmazzon. A még futó tranzakciók által módosított adatok csak a tranzakció saját bufferében, csak a tranzakció által elérhető módon kerül letárolásra. A olvasási művelet esetén, ha a tranzakció még nem módosította a keresett objektumot, akkor az adatbázisból, a konzisztens adatbázisból kell kivenni az értéket. Ebben az esetben szükség esetén az objektum időpecsétjét ellenőrizni, módosítani kell. Ha az olvasó tranzakció időpecsétja kisebb lenne, mint az objektum már beállított írási időpecsétje, akkor a tranzakció nyilvánvalóan megsérteti a TO elveket, ezért nincs értelme tovább futtani,hiszen véglegesítéssel már nem fejeződhet be. Ezért a TM kénytelen abortálni az olvasást igénylő tranzakciót. Ha viszont az igénylő időpecsétje nagyobb, mint az írási időpecsét, akkor elvégezhető a művelet és ha az igénylő időpecsétje nagyobb, mint az objektum olvasási időpecsétjelzője, akkor a jelzőt is át kell állítani ezen új időpecsét értékre. Ha a tranzakció elérkezik a lezárásához és a commit utasítást kell végrehajtani, akkor a TM megpróbálja véglegesíteni a tranzakció műveleteit. Ehhez sorba vesz minden módosított objektumot a saját munkaterületérő, majd mindegyikre ellenőrzi, hogy az adatbázisban való véglegesítéskor nem sérül-e meg a TO elv. A TO elv akkor sérül meg, az adatbázisban már nagyobb olvasási vagy írási jelző érték szerepel. Ebben az esetben hamisnak bizonyult az optimista feltételezés, ezért a TM kénytelen a lezárást kérő tranzakciót abortálni. Ha egyik műveletnél sem sérül meg a TO elv, akkor az adatbázisban is átíródnak az obejktumoka z új értékre, s ezzel együtt az objektumok írási jelző is felveszik a lezáródó tranzakció időpecsét értékét. Az optimista TO-val biztosítható a ACA, s vele együtt az RA history is, hiszen bármely tranzakció csak véglegesített adatokat olvashat. A végelegesítés előtt a többi tranzakció nem láthatja a módosításokat. E megfontolásokból mondhatjuk, hogy az ST history is teljesül. A lost update jelensége is kiküszöbölhető ezzel a mechanizmussal, mint az példa is mutatja. Megint csak az ismerős history gráfot véve, elemzzük a végrehajtás menetét. r1(x) -> w1(x) -> c1 ^ | | v c0 -> r2(x) w2(x) -> c2 Előbb r2(x) jön, ami beállítja az x olvasási jelzőját 2-re. Ezt követi r1(x), amely szintén engedélyezett, s végre is hjtódik, de hatására nem fog módosulni x olvasás jelzője. Ezután w1(x) elvégzi amódosítást, de az eredmény T1 saját munakterületére kerül. Ha most jön c1, akkor a TM megpróbálja véglegesíteni a módosításokat. Mivel x olvasási jelzőjében mát a 2 érték szerepel, a módosítást pedig az 1-es sorszámú tranzakció kezdeményezi, ezért itt megsérül a TO elv. Emiatt a TM kénytelen a T1 tranzakciót visszagörgetni. Az aadtbázisan tárolt kép nem fog változni. A T1 majd egy nagyobb sorszámmal újra fog indulni. Ezután T2 is elvégzi a módosítást, ő is a saját munkaterületén. Mikor c2 következik, a TM újból ellenőriz, s mivel most az olvasási jelző 2, az írási pedig ennél is kisebb, engedélyezni fogja a módosítást. Ennek hatására T2 véglegesítődhet, s az x elem írás jelzője is felveszi a 2 értéket. A TO módszernek, s az optimista módszernek is számos változata létezik még az említett főbb irányok mellett, melyek apróbb technikai elemekben eltérhetnek a megadott módszerektől. Az elméleti alapmódszerek áttakintése után figyelmünket egy konkrét rendszer felé, az Orcale rendszer felé fordítjuk, s áttekintjük az Oracle tranzakció kezelésének alapjait. Oracle rendszer A Oracle RDBMS rendszer 7-es verziójára támaszkodva betekintést adunk egy gyakorlatban is megvalósított és a gyakorlatban is bevált tarnzakció kezelési módszerbe. A megvalósított elemekben sok ismerős elemet fedezhetünk fel a korábban megismert módszerekből, de lesz egy-két újdonság, és új ötlet is benne. Az Oracle TM rendszere alapvetően a zárolási mechanizmuson alapszik, de emellett felfedezhető benne bizonyos TO elemek is. A zárolás jellemzője, hogy a klasszikus zárolási módszerektől eltérően csak írási zárolás létezik. Az Oracle TM rendszere ugyanis azt a célkitűzést követi, hogy a legnagyobb konkuranciát biztosítsa, melyben egy olvasási művelet sohasem gátolja az egyéb olvasási vagy írási műveleteket. Az Oracle rendszernél tehát csak az írási műveletek között van akadályoztatás. E koncepciót megvalósítva egy olvasási műveletet bármikor végre lehet hajtani, egy olvasás nem várakozik más műveletekre és egy olvasás miatt más műveletek sem várakoznak. Az Oracle ezt a képességet úgy biztosítja, hogy hasonlóan az optimista TO módszerhez, az adatbázisban létezik az adatoknak egy konzisztens képe, amely a már lezárt tranzakciók eredményét tartalmazza csak. Így ebben a konzisztens képben, a még futó tranzakciók hatásai, módosításai nem látszanak. Az olvasási műveletek mindíg konzisztens képet adnak. Az optimista TO-tól eltérően azonban itt nem rendelkezik minden egyes tranzakció saját munkaterülettel. Így a módosítási utasítások az Oralce TM-nél egy közös munkaterületen kerülnek bejegyzésre, s az elvégzett módosítások csak a tranzakciók véglegesítésekor válnak a konzisztens kép részévé. A közös munkaterületen minden objektum csak egy példányban foglal helyet, így egyidejűleg csak egy tranzakció módosíthat egy objektumot. Az ST elvek betartásához gondoskodni kell arról, hogy egyT2 tranzakció csak olyan objektumot módosíthat, melyet egy már lezárt T1 tranzakció módosított utóljára. Ezen követelmény betartását pedig úgy biztosítja a TM, hogy addig nem enged egy objektumhoz hozzáférni, amíg az őt utóljára módosított tranzakció le nem záródik. Vagyis a TM a közös munkaterületen zárolja az egyes ojbektumokat. Az Oracle rendszer az objektumok zárolásánál a kétfázisú zárolás, 2PL mechanizmusát alkalmazza. A 2PL zárolás vizsgálatakor láttuk, hogy biztosítani tudja az igényelt biztonságot az ACID elvek betartásánál. Ott azonban az olvasások is zárolásokhoz vezettek, az Oracle rendszer esetén azonban az olvasás nem zárolja az objektumot. Az Oracle TM rendszerében ezért nincs információnk arról, hogy egy objektumon mely tranzakciók végeztek olvasási műveleteket. Az olvasási zárolások eltörlésével valóban megnövelhető a végrehajtási konkurencia és a párhuzamosság szintje, azonban ezzel együtt elveszítünk bizonyos biztonságot a tranzakciók kezelésénél. . Így például a TM nem tudja a korábban sokszor idézett lost update jelenséget kivédeni. A jelenség bemutatásához vegyük az alábbi SQL műveletsort T1 T2 SELECT X INTO :PV FROM TT WHERE K=E1; SELECT X INTO :PV FROM TT WHERE K=E1; PU := PV + 1000 PU := PV + 2000 UPDATE PV SET X = PU WHERE K=E1; UPDATE PV SET X = PU WHERE K=E1; COMMIT; COMMIT; Feltesszük,hogy a rendszerben csak ez a két tranzakció fut. Így a két párhuzamosan futó tranzakció ugyanazon x értéket kapja meg a beolvasáskor. Ezután mindkét tranzakció kiszámol egy értéknövelést az x értékére. Ezután elvégzik a módosításokat. Mivel közös munkaterület van, az első UPDATE, pl. a T1 beli UPDATE zárolja az x objektumot, de a zárolás feloldódik, ha a T1 tranzakció véglegesítődik. Ezalatt T2 türelmesen várakozik, s T1 lefutása után T2 is kiírja x-be a saját módosítását. Végeredményben tehát a kétféle értéknövelés közül csak az egyik hatása meradt meg az adatbázisban. Az egyik módosítás elveszett. Ez a tapasztalat arra mutat rá, hogy a tervezőnek éles esetekben jól át kell gondolnia, hogy elegendőek-e a rendszer által alapesetben nyújtott mechanizmusok, s ha nem akkor más módszerrel kell érvényt szerezni a kívánt biztonsági szintnek. A példánk esetén is a fejlesztőnek feladata, hogy esetleg a programok szinkronizálásval vagy esteleg egy explicit zárolással kerülje el a lost update jelenségét. Az Oracle TM a zárolásnak két finomsági szintjét különbözteti meg. Alapesetben a zárolás az érintett rekordokra vonatkozik, tehát rekord szintű zárolást használ a TM. A felhasználónak azonban lehetősége van explicit módon táblára vonatkozó zárolást is kérnie. Ebben az esetben a tábla egyetlen egy rekordja sem módosítható más tranzakciók által. A tábla zárolását egy SQL utasítással lehet kétni, amelynek formátuma: LOCK TABLE tábla IN mód; ahol a mód a zárolás módját jelenti. A lehetséges módok közül a legfontosabb a teljes zárolás módja, amikor kizárólagosan csak a zárolást kiadó utasítás végezhet módosítást az adattáblán. Ezen mód kiadásának parancsa: LOCK TABLE tábla IN EXCLUSIVE MODE; A megadott zárolás mindaddig érvényben marad, amíg le nem zárjuk az utasítást kiadó tranzakciót egy COMMIT vagy ROLLBACK utasítással. A LOCK TABLE utasítás maga is várakozásra kényszerülhet, ha más tranzakciók már foglalják az adattábla valamely részobjektumait. A nem kívánt várakzotatások elkerülésére a LOCK TABLE utasításhoz megadható egy NOWAIT opció is, melynek hatására hibajelzéssel rögtön visszatér a LOCK TABLE utasítás, ha a nem tudja a táblát a parancs kiadásának időpontjában lefoglalni. Mivel a zárolások szükségképpen együtt járnak a deadlock veszélyével, ezért az Oracle TM is felkészült a deadlock jelenségek felismerésére és elkerülésére. A lehetséges módszerek közül a TM mind a timeout mind pedig a WFG módszert alkalmazza. A deadlock feloldása az egyikigénylő műveletet sikertelenül befejezi, ami bizonyos esetekben a tranzakció visszagörgetsét is magával hozza, de például egy SQLPLUS környezetben csak a parancs kerül elvetésre, a tranzakció korábbi utasításai érvényben megmaradnak. Az egyes alap SQL utasításokhoz kapcsolódó zárolásokat mutatja be a következő lista. ------------------------------------------------------------------ SELECT nincs zárolás ------------------------------------------------------------------ UPDATE INSERT DELETE érintett rekordok zárolása ------------------------------------------------------------------ SELECT .. FOR UPDATE előjegyzés zárolásra (nem lehet ezután kizárólagos zárolás más részéről) ------------------------------------------------------------------ LOCK TABLE xxx IN EXCUSIVE MODE [NOWAIT] tábla kizárólagos lefoglalása ------------------------------------------------------------------ SET TRANSACTION READ ONLY tranzakció szintű olvasási konzisztencia ------------------------------------------------------------------ SAVEPOINT xxx közbenső állomás a tranzakciónál ------------------------------------------------------------------ ROLLBACK TO SAVEPOINT xxx visszagörgetés a SAVEPOINT-ig ------------------------------------------------------------------ A táblában szereplő SET TRANSACTION READ ONLY utasítás a következő problémára ad segítséget. A hosszabb tranzakciók esetén nagy valószínűséggel megváltozik a konzisztens adatbáziskép tartalma. Ez azt is jelenti, hogy a trnzakció elején kiadott lekérdezési utasítás más eredményt ad vissza, mint a tranzakció későbbi időpontjában ismételten kiadott lekérdezési utasítás. Enek oka, hogy menetközben más tranzakciók sikeresen befejeződtek, így már azok hatásai is láthatóvá váltak a többiek számára is az adatbázisban. Emiatt változik az adatbázis képe a tranzakció végrehajtása során. Bizonyos esetekben azonban előnyösebb lenne, ha végig ugyanazt a képet látná a tranzakció. Ha a tranzakció teljes időtartalma alatt ugyanazt az adatbázis képet látja a tranzakció minden olvasási művelete, akkor tranzakció szintű olvasási- konzisztenciáról beszélünk. A tranzakció szintű olvasási konzisztencia mellett létezik egy műveleti szintű olvasási konzisztencia is, melyben csak a művelet végrehajtásának idjáre biztosított, hogy a tranzakció ugyanazt az adatbázis képet látja. Ez feltétlenül szükséges is, hiszen komoly zavarokat okozhatna, ha egy több táblát is érintő művelet során az egyik táblán még a korábbi konzisztens állapotot, míg a másik táblán már az új konzisztens állapotot olvashatja a tranzakció. A műveleti szintű olvasási konzisztenciát az Oracle alapérelemzés szerint biztosítja. A tranzakció szintű olvasási konzisztenciát azonban külön igényelni kell a tranzakciónak. A SET TRANSACTION READ ONLY utasítás hatására egy új tranzakció kezdődik el, melyben már a tranzakció szintű olvasási konzisztencia szabályai érvényesülnek. E tranzakció jellegzetessége, hogy csak olvasási műveleteket tartalmazhat, vagyis a tranzakció műveletei között nem szereplehet módosítási, törlési, bővítési utasítás. A tranzakció szintű olvasási konzisztencia biztosítása nagyobbb erőforrás ráfordítást igényel,mint a műveleti szintű, hiszen sokkal korábbi adatbázis állapotok képét is meg kell őrizni a tranzakció számára. A korábbi álllapotok megőrzésére az Oracle a SCN -en alapuló mechanizmust használja. Az Oracle TM minden olvasási konzisztenciát igénylő végrehajtási egységhez, tranzakcióhoz vagy művelethez feljegyzi, hogy mikor, mely időpontban indult el. Emellett a konzisztens képbe kikerülő adatelemek, melyek rendszerint adatbázis blokk egységekben tárolódnak, szintén feljegyzésre kerül, mely időpontban kerültek be a konzisztens adatbázisképbe. Ezen időpont jelzőket nevezik SCN-nek. A SCN (system change number) egy időpecsét jellegű szerepet ttölt be. Mivel egy hosszabb idejű tranzakció futása alatt több lezárt tranzakció is módosíthatta az érinett blokkot, ezért a blokknak több, különböző SCN azonosítójú példányát is nyilván kell tartani. A lekérdezési művelet során a TM mindíg az indulási SCN értéknek megfelelő adatblokkokat fogja felhasználni. A következő ábra az SCN alapú mechanizmust szemlélteti. Mivel a művelet a 982-es SCN értéket kapta, így azon blokkokat fogja a lekérdezésnél felhasználni, melyek SCN száma nem nagyobb a 982 értéknél. Az ábrából is látható, hogy a különböző SCN változatú adatblokkok megőrzése jelentős helyigénnyel jár, ezért hosszabb tranzakciók esetén, ha azok tranzakció szintű olvasási konzisztenciát igényelnek, menetközben betelhet az Oracle tároló buffere is. Ekkor nem folytatható tovább a tranzakció szintű konzisztencia biztosítása sem. Még a tranzakció kezeléshez kapcsolódóan röviden megemlítjük a táblázatban szereplő másik új elem, a SAVEPOINT utasítás jelentését is. A tranzakcióknak a sikertelen műveletek esetén érvényteleníteni kell az esetleg tévesen elvégzett vagy félig kész adatokat, hogy újra, előlről indulva elvégezzék a kijelölt műveleteket. A hiba eseténtehát vissza kell görgetni az eddig elvégzett tevékenységeket,hogy újra konzisztens állapotba kerüljünk. Hosszú tranzakciók esetén es igen nagy veszteséget jelenthet időben és energiában is. Az Oracle TM e veszteségek csökkentésére vezette be a savepoint lehetőséget. A savepoint a tranzakció egy tetszőleges pontját jelöli ki, s egy tranzakcióban többb savepoint is lehet, ezzel úgymond szeletekre darabolva a tranzakciót. A savepoint létrehozása a SAVEPOINT azonosító utasítáásal lehetséges. A savepoint jelentősége abban áll, hogy alkalmazása esetén a hibás felléptekor a műveleteket nem szükséges a tranzakció kezdetéig visszagörgetni, a TM utasítható a megelőző valamelyik savepoint-ig történő visszagörgetésre is. E tevékenységet a ROLLBACK TO SAVEPOINT azonosító utasítással aktivizálhatjuk. Ennek hatására csak a savepoint után történt tevékenységek hatásait, zárolásait szünteti meg a TM. Ezután újra meg lehet próbálni a visszagörgetett műveletek végrehajtását. A zárolásal kapcsolatosan még annyit megjegyzésként hozzáteszünk az elmondottakhoz, hogy nemcsak a DML utasítások esetén kell a zárolást figyelmbe venni, hanem a DDL utasításoknál is. Így például tábla szerkezetét módosítani, vagy a táblát törölni csak akkor lehet, ha a tábla minden eleme szabad, egyetlen egy rekordja sincs zárolva. Maga a DDL utasítás azonban csak a művelet idejére zárolja a táblát, a művelet befejezése után a tábla ismét szabadon használható. Az zárolásokra ad érdekes példát az az eset amikor több különböző tranzakció ugyanazt a táblát bővíti. Egy mindenemű integritási feltétel nélküli tábla esetén a tranzakciók nyugodtan dolgozhatnak egymás mellett, hiszen különböző rekordokat visznek fel. Ha azonban beállítjuk valamely mezőre a PRIMARY KEY integritási feltételt, és mindkét tranzakció egy-egy olyan rekordot kíván felvinni, melyekben a kijelölt mező azonos értékű, akkor az első tranzakció megteheti a felvitelt, de a másodiknál a művelet kiadása után a tranzakció nem hibejelzést kap, hanem várakozásra kényszerül. A második tranzakció csak akkor fut tovább, ha az első is már leállt. S amikor az első leállt,akkor kapjuk a második tranzakciónál a hibaüzenetet, hogy a megadott mezőérték már létezik a táblában. A zárolások gyakorlati hatásának szemléltetésére és a megusmert szabályok gyakorlására egy műveletsort mutatunk be, melyben mindíg két párhuzamos tranzakció fut. A példában feltesszük, hogy az A és a B session is ugyanazon felhasználóhoz tartozuk, azaz mindkét tranzakció egyforma hozzáférési jogosultságokkal rendelkezik. A példában feltesszük még azt is, hogy a felhasználónak induláskor még nem létezik TEST adattáblája. Az SQL utasítás utáni sorban a rendszertől kapott választ adjuk meg, azt feltételezve, hogy a műveletsort SQLPLUS környezetben futtajuk le. A session B session T2: SELECT * FROM TEST; hibaüzenet, hogy a TEST még nem létezik T1: CREATE TABLE TEST (A NUMBER(5)); a művelet sikerült T2: SELECT * FROM TEST; a tábla üres (tehát már látja T2 is táblát, de tartlma üres) T1: INSERT INTO TEST VALUES (5); a művelet sikerült T2: INSERT INTO TEST VALUES (6) a művelet sikerült T1: SELECT * FROM TEST; a válasz: 5 (vagyis csak a saját módosítás látható még) T2: SELECT * FROM TEST; a válasz: 6 (vagyis csak a saját módosítás látható még) T1: COMMIT; a művelet sikerült T2: SELECT * FROM TEST; a válasz: 5 6 (T1 módosítása is látható már) T2: ROLLBACK; a művelet sikerült T3: SELECT * FROM TEST; válasz: 5 (T2 hatása visszagörgetődött) T4: SELECT * FROM TEST; válasz: 5 T4: LOCK TABLE TEST IN EXCLUSIVE MODE; a művelet sikerült (a táblában csak T4 módosíthat) T4: INSERT INTO TEST VALUES (9); a művelet sikerült T4: SELECT *FROM TEST; válasz: 5 9 T3: SELECT * FROM TEST; válasz: 5 T3: INSERT INTO TEST VALUES (7); a T3 várakozik (T4 lefoglalta a táblát, T3 vár a tábla felszabadítására) | T4: INSERT INTO TEST VALUES (8); | a művelet sikerült | | T4: COMMIT; v művelet sikerült zárolás felengedve, T3 továbbfut, elvégzi a rekordfelvitelt T3: COMMIT művelet sikerült T6: SELECT * FROM TEST; válasz: 5 8 9 7 T6: LOCK TABLE TEST IN SHARE MODE; művelet sikerült T5: LOCK TABLE TEST IN EXCLUSIVE MODE; T5 várakozik (T6 fogja a táblát) | T6: DELETE FROM TEST WHERE A=9; | művelet sikerült | T6: COMMIT; v művelet sikerült zárolás felengedve, T5 nyugtázza előző műveletét T7: LOCK TABLE TEST IN EXCLUSIVE MODE; T7 várakozik (T5 fogja a táblát) T5: INSERT INTO TEST VALUES (11); | művelet rendben | T5: ROLLBACK; | művelet rendben v zárolás vége, T7 nyugtázza a műveletét T7: ROLLBACK művelet rendben T9: LOCK TABLE TEST IN SHARE MODE; művelet rendben ( a táblát osztottan foglaltuk le, azaz más tranzakció is lefoglalhatja osztott módra, de a táblát teljes, kizárólagos lefoglalása már nem megengedett, s más tranzakció módosításokat sem végezhet benne) T8: LOCK TABLE TEST IN SHARE MODE; művelet rendben T9: DELETE FROM TEST WHERE A = 5; T9 várakozik T8: DELETE FROM TEST WHERE A = 5; | T8 várakozik | | | v T9 ekkor deadlock üzenettel leállítja műveletét T8 tovább vár | T9: ROLLBACK; | művelet rendben v T8 nyugtázza műveletét T8: ROLLBACK; művelet rendben T10: DELETE FROM TEST WHERE A=5; művelet rendben T11: SELECT * FROM TEST; válasz: 5 8 7 T11: DELETE FROM TEST WHERE A = 5; T11 várakozik (T10 foglalja a rekodot) | | v T10: COMMIT; művelet rendben T11 visszajelez, hogy 0 sor törölve (zárolás felenfedve) T11: COMMIT; művelet rendben T13: SET TRANSACTION READ ONLY; művelet rendben T13: SELECT * FROM TEST; válasz: 8 7 T12: INSERT INTO TEST VALUE (2); művelet rendben T13: SELECT * FROM TEST; válasz: 8 7 T12: COMMIT; művelet rendben T13: SELECT * FROM TEST; válasz: 8 7 (nem látszik a lezárt tranzakció eredménye) T13: INSERT INTO TEST VALUES (6); hiba, T13 csak olvashatja az adatokat T13: ROLLBACK művelet rendben T14: SELECT * FROM TEST; válasz: 8 7 2 (most már látható T12 eredménye) T14: INSERT INTO TEST VALUES (8); művelet rendben T15: ALTER TABLE TEST ADD (B CHAR(2)); hiba a tábla foglalt (T14 foglalja) T15: COMMIT; művelet rendben T14: COMMIT; művelet rendben Recovery mechanizmus Az eddigiekben ugyan részletesen szóltunk a különböző tranzakció kezelési mechanizmusokról, azonban elhallgattunk egy igen lényeges elemet, ami nélkül a traznakciókezelés sem működhet az elvárásoknak megfelelően. Pontosabban nem elhallgattuk, inkább túlságosan nagyvonalúan érintettük csak a különböző módsszerek leírásánál. Ez a hiányolt láncszem a recovery mechanizmus, vagyis az adatok konzisztenms állapotba történő visszaállításának mechanizmusa. A különböző TM stratégiáknál igen gyakran említettük, hogy bizonyos esetekben a tranzakciókat vissza kell görgetni, a tranzakciók abortálódhatnak, de közelebbről sehol sem említettük, hogy hogyan is történhet az elvégzett algoritmusok visszagörgetése. Pedig ez a feladat sem annyira triviális, hiszen tekintettel kell lenni a már korábban elvégzett és lezárt műveletekre és a még futó ás tranzakciókra is. Mivel a különböző tranzakciókezelési módszerek más és más visszagörgetési technikát igényelnek mi most az Oracle rendszerben megvalósuló mechanizmust fogjuk közelebbről megismerni. Mindenek előtt azt kell tisztázni, hogy milyen célt is szolgál a recovery mechanizmus. Az ugyan nyiolvánvaló, hogy egy tranzakció abortálásakor szükség van az elvégzett módosítások megsemmísítésére, vagyis egy korábbi konzisztens állapot visszaállítására. Emellett azonban akkor is szükség lehet egykorábbi konzisztens állapot helyreállítására, ha a rendszerrel történik olyan váratlan hibaesemény, melynek közvetkeztében nem tudja a TM a normális munkát továbbfolytatni. Ílyen hibaesemény lehet például egy rendszer összeomlás, amikor is a futó tranzakciók módosulásai a közös munkaterületen elvésznek, így már nem lehet azokra építve továbbfolytatni a history végrehajtást. Ekkor nincs más választás, mint a rendszernek egy korábbi konzisztens állpotba való visszahelyése, melynek eléréséhez minden még futó tranzakciót visszagörget és a már lezárt tranzakciót véglegesít a tranzakció menedzser. Ez utóbbira azért van szükség, mert az Oracle TM mechanizmusából következően a lezárt tranzakciók eredményei nem rögtön íródnak az adatbázis állományba, hanem úgymond aszinkron módon kerül át a végelgesített adat a memóriából az adatbázisállományba. Emiatt a rendszerősszeomlás pillanatában is előfordulhat olyan Comiit-tal lezárt adatmódosítás, mely még nem került át az adatbázis állományba. A rendszer újraindításakor gondoskodni kell ezen adatoknak az előállításáról és az adatbázis állományba történő átviteléről. Még talán az előzőeklnél is nagyobb károkozással járhat, ha az adatlemez, az adatbázis állományok sérülének meg. A helyesen felépített és a kellően akrban tartott adatbázisok esetén ekkor is van lehetőség az elveszett adatok egy bizonyos mértékű visszaállítására, ahol a veszteség mértéke jelentősen függ a sérült területektől és a rendelkezésre álló mentésektől. Összefoglalóan megállkapíthatjuk, hogy a recovery mechanizmusra az adatbáziskezelő rendszerekben három szempontból is szükség van, neveztesen - az egyes tranzakciók visszagörgetéséhez - a rendszerösszeomlás után a rendszer újrafelépítéséhez - az adathordozók sérülése utáni helyreállításnál. A recovery mechanizmusát vizsgálva nyilvánvaló, hogy csak a korábbi állapotok megőrzésével, a korábbi mentésekre alapozva lehet a visszaállításokat is elvégezni. Az Oracle esetén a mentéshez szükséges információk begyüjtésének, kezelésének folyamatát szemlélteti a következő ábra. Az ábrában alul helyezkednek el a rendszerhez tartozó adatálloományok. Itt most két alapvető állománytipust különböztetünk meg. Az egyik a tényleges adatbázis, az adatbázis objektumokkal, míg a másik egy naplózási állomány. Az ábra felső része, az SGA terület egy memóriában elhelyezkedő területet mutat, amely az RDBMS felügyelete alatt áll. Ez tulajdonképpen az Oracle szerver munkaterülete. Az SGA területe több különböző felaadatot ellátó részre van oisztva, amiből itt most háromkerült kiemelésre. Az egyik az közös munkaterület,amelyben a futó tranzakciók által végzett módosítások találhatók meg. A másik rész egy visszagörgetési, ROLLBACK terület, amelyre az érintett objektumok módosítás előtti állapotai kerüólnek bele. A harmadik terület egy naplózási terület, amleyben inden, az adatbáézist érintő művelet bejegyzésre kerül a későbbi mentésekhez. Vegyünk egy egyszerű tranzakciót a rendszer működésének bemutatására. A tranzakció a következő utasításokból álljon: UPDATE AUTO SET AR=1435 WHERE RSZ='RSt345'; COMMIT; A végrehajtás a következő lépésekből áll. Elsőként a rendszer megnézi, hogy a módosítandó objektum ben van-e a memóriában, az SGA területen. Ha nem lenne előbb be kell hozni a megfelelő adatlapot. Ha már benn van a módosítandó objektum, akkor benn a memóriában átírható a korábbi értéke az új értékre. Így megadott autorekorban az AR mező felveszi a 1435 értéket. A módosítás elvégzése előtt azonban a rendszer még előbb kimenti a ROLLBACK területre az objektum korábbi értékét, a 1212-t.. Ezután kerül sor a memórán belüli módosításra. Az elvégzett tevékenységeket a rendszer a REDOLOG területen naplózza, vagyis feljegyzi,hogy milyen módosításokat végeztünk a ROLLBACK és az adat területen. A naplózás befejzése után kerülhet sor a következő művbeletre a COMMIT utasításra. A COMMIT hatására véglegesítődik a tranzakció hatása. Mivel már nem lehet visszagörgetni a tranzakciót ezért nincs már szükség a ROLLBACK szegmensbeli eljegyzésekre, ezért azok törlődnek. A REDOLOG területre pedig bekerül a COMMIT utasítás végrehajtása is. A tranzakció lezárásakor gondokodni kell arról, hogy az adatok most már mindíg rendelekzésre álljanak, ezért a napló állományba rögtön kimentésre kerül a tranzakcióhoz tartozó feljegyzés sorozat. Maga az adatérték azonban csak aszinkron módon íródik ki az adatbázisállományba, így annak értéke nagyvalószínűséggel nem fog még módosulni a COMMIT kiadása után. Nézzükmost azt meg, hogy az egyes szituációkban milyen módon lehet a lementett információkat felhasználva adathelyreállítást végezni. A tranzakció abortálása eseténigen egyszerű a helyzet, mivel a ROLLBACK szegmensben benne van minden módosított adatelem induló értéke, így a TM- nek nem kell mást csinálnia, mint végigolvasni a ROLLBACK terület ezen tranzakcióhoz tartozó bejegyzéseit, s az ott letárolt értékeket kell beírni a közös memória adatterület megfelelő objektumába. Az induló értékek vissszaíráésa után a ROLLBACK területbeli és a REDOLOGterületbeli feljegyzése is szükségtelenné váltak, így azok is törlődnek. Ezzel a rendszer törölte a tranzakció minden hatását. Rendszerösszeomlás esetén egy kicsit összetettebb a helyzet. A helyzetet az bonyolítja meg, hogy a REDOLOG állományba a TM működési mechanizmusából következően nemcsak a véglegesített tranzakciókra vonatkozó bejejgyzések kerülhetnek ki. Ugyan később egyértelműen eldönthető, hogymelyik tranzakció záródott le, hiszen azok tartalmaznak COMMIT bejegyzést is, de a visszamentésnél gondolni kell a le nem zárul t tranzakciókra vonatkozó bejegyzésekre is. A visszamentés folyamatát szemlélteti a következő ábra. A rendser újraindítása során az Oracle sorba veszi azon műveleteket a REDOLOG napló állományból, melyek hatása még nem került át az adatbázis állományba. Mivel a naplóállományban minden esemény pontosan le van tárolva, ezért a visszajátszás megoldható. Ha napló COMMIT utasítást is tartalmaz a tranzakcióra, akkor újból véglegesítődik a tranzakció. Ha azonban nem talál COMMIT bejegyzést a rendszer, akkor egy ROLLBACK utasítással visszagörgetődik az újrajátszott műveletsor is. Ezzel ugyan kicsit több idő megy el a visszaállításra, de e mechanizmus lényegesen egyszerűbb napló kezelést tesz lehetővé. A lemezen elhelyezkedő állományok sérülése esetén a REDOLOG állomány segítségével az előzőekben leírt módon újra lehet játszani a benne tárolt műveleteket. Mivel a napló állomány terjedelmi is véges, ezért nem lehet tetszőlegesen régi időkre bvisszanyúlóan tárolni bennük a lépéseket, emiatt az adatbázis állományt is rendszeresen menteni kell, hogy meglegyen azon adatbázis állapot, amelyhez már létezik naplóbejegyzés. Ha ez a rendszerállapot nem áll rendelkezésre, akkor a helreállítás sem oldható meg.