Podcast na Adatbázis-tranzakciók, SQL és architektúrák
Adatbázis-tranzakciók, SQL és Architektúrák – Komplett Útmutató
Podcast
Adatbázis-tranzakciók, SQL és architektúrák
Délka: 16 minut
Přepis
Kristýna: ...és pontosan ezért zseniális az egész! Vagy minden megvalósul, vagy semmi. Nincsenek félmegoldások.
Martin: Várj, szóval amikor pénzt küldök netbankon keresztül, az a művelet, az pontosan ez? Vagy elmegy a pénz a számlámról és megérkezik a másikra, vagy egyszerűen nem történik semmi, és nálam marad a pénz. Semmi átmenet?
Kristýna: Pontosan! Fején találtad a szöget. Ez a tranzakciók teljes koncepciójának a szíve.
Martin: Ez szuper! Oké, ezt az elejétől kell kibontanunk. A StudyFi Podcastot hallgatjátok, és ma belevetjük magunkat az adatbázis-tranzakciók világába.
Kristýna: Szóval, Martin, a bankos analógiád tökéletes volt. Egy tranzakció lényegében olyan műveletek csoportja, amik logikailag összetartoznak, és egy egységként kell végrehajtani őket. Egyetlen, széttörhetetlen egységként.
Martin: Mint egy atom, amit nem lehet tovább osztani?
Kristýna: Pontosan! És ez el is vezet minket a tranzakciók első és legfontosabb tulajdonságához. Ezt atomicitásnak hívják. Képzeld el, hogy át akarsz utalni ezer koronát. Mi kell, hogy történjen?
Martin: Hát... a számlámról le kell vonni ezer koronát, és a barátom számlájára hozzá kell adni ezer koronát. Ez két művelet.
Kristýna: Így van. Az atomicitás pedig garantálja, hogy vagy mindkettő megtörténik, vagy egyik sem. Ha a rendszer összeomlana azután, hogy levonták tőled a pénzt, de még azelőtt, hogy jóváírták volna a barátodnak... az gond lenne, igaz?
Martin: Az nagy gond lenne! Én elveszíteném a pénzt, ő pedig nem kapná meg.
Kristýna: És pontosan ezért van a tranzakció. Ha bármelyik része nem sikerül, az egész tranzakció visszakerül az eredeti állapotába. Ezt hívják ROLLBACK-nek. Az adatbázis abba az állapotba kerül, amiben a tranzakció megkezdése ELŐTT volt. Mintha soha meg sem történt volna.
Martin: Szóval a tranzakció egyfajta biztosíték, ami konzisztens, vagyis helyes és logikus állapotban tartja az adatbázist. Egy jó állapotból egy másik jó állapotba viszi át.
Kristýna: Pontosan. A konzisztencia kulcsfontosságú. Enélkül káosz lenne az adatokban.
Martin: Rendben, az atomicitást értem. Minden vagy semmi. Mi a következő? Milyen más szuperképességei vannak még a tranzakcióknak?
Kristýna: Szuperképességek, ez tetszik. A következő az izolált visszaállíthatóság, vagyis az izoláció. Ez azt jelenti, hogy egy folyamatban lévő tranzakció nem látja azt a rendetlenséget, amit egy másik, párhuzamosan futó tranzakció csinál.
Martin: Hogy érted, hogy rendetlenség?
Kristýna: Képzeld el, hogy két tranzakció ugyanazokkal az adatokkal dolgozik. Ha az egyik látná a másik még félkész, nem megerősített változásait, akkor rossz, ideiglenes adatokkal dolgozhatna. Az izoláció biztosítja, hogy minden tranzakció úgy érezze, mintha egyedül lenne az adatbázisban.
Martin: Aha, szóval nem másznak egymás tyúkszemére. És mi történik, ha egy tranzakciónak vissza kell állnia, vagyis ROLLBACK-et kell csinálnia? Az nem befolyásolja a többit?
Kristýna: Ideális esetben nem. De ha egy tranzakció olyan adatokkal dolgozna, amiket egy másik tranzakció megváltoztatott, majd visszavont, akkor az elsőnek is vissza kellene állnia. Ezt hívják láncreakciós visszaállításnak vagy dominóeffektusnak. Ez olyasmi, amit a rendszerek igyekeznek megakadályozni.
Martin: A dominóeffektus... drágán hangzik.
Kristýna: Lehet. A harmadik tulajdonság a tartósság, vagy angolul durability. Ez egyszerű – amint egy tranzakciót sikeresen befejeztek és COMMIT paranccsal megerősítettek, a változásai véglegesen tárolódnak az adatbázisban. Túlélnek egy áramszünetet vagy egy szerver újraindítást is.
Martin: Szóval a COMMIT az a végső pecsét: „Kész, örökre mentve.”
Kristýna: Pontosan így van. Az utolsó tulajdonság pedig a szerializálhatóság, ami főleg elosztott adatbázisoknál fontos. Egyszerűen fogalmazva azt jelenti, hogy még ha sok tranzakció fut is egyszerre és összefonódik, az eredménynek ugyanannak kell lennie, mintha szépen, egymás után, valamilyen sorrendben hajtották volna végre őket.
Martin: Említetted az elosztott adatbázisokat. Az valami Star Wars-os dolognak hangzik. Mi az?
Kristýna: Valójában elég gyakori. Képzelj el egy nagy céget, aminek fiókjai vannak szerte a világon. Az adatbázis nem egyetlen szuperkomputeren van, hanem kisebb részekre, fragmentumokra van osztva, amik különböző szervereken tárolódnak, ezeket hívjuk csomópontoknak.
Martin: Szóval a prágai adatok egy prágai szerveren vannak, a New York-i adatok meg egy New York-i szerveren?
Kristýna: Pontosan. És most képzeld el, hogy egy olyan műveletet akarsz végrehajtani, ami mindkét város adatait érinti. Például egy alkalmazottat Prágából New Yorkba helyezni át. Ez egy globális tranzakció.
Martin: Mert átnyúlik egy csomóponton.
Kristýna: Igen. És ez a globális tranzakció részleges, lokális tranzakciókra bomlik. Egy lokális tranzakció Prágában zajlik – eltávolítja az alkalmazottat. A másik New Yorkban – hozzáadja. És az egésznek atomikusan kell működnie. Vagy mindkettő sikerül, vagy egyik sem.
Martin: És ki irányítja ezt? Kell lennie valami főnöknek, nem?
Kristýna: Persze. Az a csomópont, ahonnan a tranzakció indul, lesz a primer csomópont. És ott fut az úgynevezett primer tranzakció, ami koordinálja az összes részfeladatot. Olyan, mint egy zenekari karmester.
Martin: És annak a karmesternek vannak segítői?
Kristýna: Vannak. Minden csomópontban van egy tranzakciókezelő modul, ami elemzi a kéréseket, szétosztja a munkát, majd összerakja az eredményeket. És van egy adatkezelő modul is, ami magáról az olvasásról és írásról gondoskodik a lokális adatbázisban.
Martin: Rendben, szóval van egy karmesterünk – a primer tranzakció – és egy lokális tranzakciókból álló zenekar. De hogyan biztosítja a karmester, hogy mindenki helyesen és ugyanabban a pillanatban fejezze be? Főleg, ha mondjuk a bolygó másik oldalán vannak.
Kristýna: Remek kérdés. Ehhez egy úgynevezett kétfázisú commit protokollt használnak. Bonyolultan hangzik, de az elv elegáns. Két fázisban működik.
Martin: Első fázis?
Kristýna: Az első fázis a szavazási fázis. A primer tranzakció megkérdezi az összes résztranzakciót: „Készen álltok a változások véglegesítésére? Befejeztétek a munkátokat, és nem láttok semmi problémát?” Minden résztranzakció elvégzi a munkáját, de a változásokat még nem menti el véglegesen. Csak előkészíti őket, és azt válaszolja: „Igen, készen állok” vagy „Nem, hiba történt”.
Martin: Szóval mindenki csak előkészíti a tollát a papír fölé, de még semmit sem ír alá.
Kristýna: Pontosan! És most jön a második fázis – a döntési fázis. Ha AZ ÖSSZES résztranzakció azt válaszolta, hogy „Igen, készen állok”, akkor a primer tranzakció globális COMMIT parancsot küld. Csak ekkor „írja alá” mindenki egyszerre, és menti el véglegesen a változásokat.
Martin: És mi van, ha csak egyetlen egy is azt válaszolja, hogy „Nem”? Vagy egyáltalán nem válaszol, mert mondjuk leállt a szerver?
Kristýna: Akkor a primer tranzakció globális ABORT parancsot küld. És mindenki, még azok is, akik készen álltak, elvetik a változásaikat. Az egész művelet visszakerül az eredeti állapotába. Tényleg vagy mindenki, vagy senki.
Martin: Ez elég robusztus. De mi történik, ha pont a második fázisban szakad meg a kapcsolat? Mondjuk a lokális csomópont parancsra vár, és a primer csomópont összeomlik.
Kristýna: Ezek pontosan a legálnokabb helyzetek. A rendszereknek vannak erre mechanizmusai, például időtúllépés, az úgynevezett time out. Ha a válasz nem érkezik meg időben, a tranzakciót megelőzően megszakítják. Ezeknek a hibáknak a kezelése az adatbázis-rendszerek egyik legbonyolultabb része.
Martin: Arról beszéltünk, hogy a tranzakciók izoláltan futnak, mintha egyedül lennének az adatbázisban. De valójában nincsenek. Hogyan intézi el a rendszer, hogy két tranzakció, ami ugyanazt a sort akarja megváltoztatni egy táblában, ne szóljon bele egymás dolgába?
Kristýna: Zárakat használ ehhez. Olyan, mint egy kulcs a tárgyalóteremhez. Amikor egy tranzakció egy objektummal akar dolgozni – mondjuk egy sorral egy táblában – először le kell zárnia. Amíg fel nem oldja, senki más nem dolgozhat vele.
Martin: Ez logikus. Szóval ha én meg akarom változtatni egy rendelés állapotát, lezárom. És ha valaki más ugyanabban a pillanatban próbálja megváltoztatni, az ő tranzakciójának várnia kell, amíg én befejezem és feloldom.
Kristýna: Pontosan. Ezt hívják kétfázisú zárolási protokollnak. Az első fázisban a tranzakció csak megszerzi a zárakat, a második fázisban pedig már csak feloldja őket. De mit gondolsz, mi történhet, ha két tranzakció több dolgot akar kölcsönösen lezárni?
Martin: Hm... Tegyük fel, hogy az A tranzakció lezárja az 1-es objektumot, és vár a 2-es objektumra. A B tranzakció pedig lezárja a 2-es objektumot, és vár az 1-es objektumra.
Kristýna: És máris van egy problémánk! Mindegyik vár a másikra, és egyik sem tud megmozdulni. Ezt hívják holtpontnak, angolul deadlock-nak.
Martin: Mint két ember, akik egy szűk utcában találkoznak, és mindegyik arra vár, hogy a másik félreálljon.
Kristýna: Tökéletes hasonlat! És a rendszernek ezt fel kell ismernie. Ezt az úgynevezett várakozási gráfok segítségével teszi. A tranzakciók a csomópontok, és egy nyíl A-tól B-ig azt jelenti, hogy „A vár B-re”. Ha ebben a gráfban kör alakul ki – A vár B-re, B vár A-ra – akkor holtpontunk van.
Martin: És mit csinál ezzel a rendszer? El kell taszítania az egyik embert az utcából, nem?
Kristýna: Pontosan. Egyszerűen „lelövi” az egyik tranzakciót. Ez azt jelenti, hogy megszakítja, végrehajtja a változásainak ROLLBACK-jét, és feloldja a zárait. Ezzel megszakad a kör, és a másik tranzakció folytathatja. A megszakítottat pedig általában újra megpróbálják végrehajtani.
Martin: Szóval a rendszer hagyja, hogy kialakuljon a holtpont, aztán megoldja. Nem lehetne valahogy megelőzni?
Kristýna: Lehet. Ez egy másik stratégia. A detektálás és megoldás helyett megpróbálhatjuk elkerülni a holtpontot. Az egyik népszerű módszer az időbélyeg. Minden tranzakció a kezdetekor kap egy egyedi időbélyeget – lényegében egy számot, ami megmondja, mikor kezdődött. Minél kisebb a szám, annál „öregebb” a tranzakció.
Martin: És mire jó ez?
Kristýna: Amikor zárolási konfliktus adódik, a rendszer megnézi az időbélyegeiket, és előre meghatározott szabályok szerint dönt. Két fő módszer létezik: a „Wait-Die” és a „Wound-Wait”.
Martin: Várj-Halj meg és Sebez-Várj? Ez durván hangzik.
Kristýna: A nevek drámaiak, de az elvek logikusak. A „Wait-Die” esetében az a szabály: ha egy régebbi tranzakció olyan zárat akar, amit egy fiatalabb tranzakció tart, akkor a régebbi vár (Wait). De ha egy fiatalabb tranzakció akar zárat egy régebbitől, akkor a fiatalabbat azonnal leállítják (Die), és később kell újra próbálkoznia.
Martin: Szóval a fiatalabbak engednek az idősebbeknek. Ez udvarias.
Kristýna: Pontosan így van. A „Wound-Wait” esetében pedig épp ellenkezőleg, az idősebb fél aktív. Ha egy régebbi tranzakció olyan zárat akar, amit egy fiatalabb tart, akkor „megsebzi” (Wound) a fiatalabbat – vagyis ROLLBACK-re kényszeríti – és elveszi a zárat. Ha a fiatalabb akar zárat egy régebbitől, akkor alázatosan várnia kell (Wait).
Martin: Mindkét esetben az idősebb tranzakcióé az elsőbbség. Csak az a különbség, hogy ki az aktív. Értem.
Martin: Rendben, az elmélet megvan. De hogyan néz ki ez a gyakorlatban? Amikor kódot írok, hogyan mondom meg az adatbázisnak: „Figyelj, most kezdődik egy tranzakció, és itt ér véget”?
Kristýna: Egyszerűbb, mint gondolnád. Az SQL nyelvben, amit a legtöbb adatbázissal való kommunikációra használnak, a tranzakció gyakran automatikusan elindul az első olyan paranccsal, ami adatot módosít.
Martin: És hogyan fejezem be?
Kristýna: Két lehetőséged van. Ha minden rendben ment, és véglegesen el akarod menteni a változásokat, akkor a végére írod a COMMIT parancsot.
Martin: Ez az a végső pecsét, amiről beszéltünk.
Kristýna: Igen. És ha valami elromlott, vagy valamilyen okból törölni akarod az összes változást a tranzakció elejétől, akkor a ROLLBACK parancsot használod.
Martin: Szóval vagy COMMIT, vagy ROLLBACK. Minden vagy semmi. Megint visszatérünk ehhez.
Kristýna: Pontosan. Van még egy hasznos dolog, ez pedig a SAVEPOINT. Egy hosszú tranzakció során létrehozhatsz ilyen mentési pontokat. Írhatsz SAVEPOINT sajat_pont;. És amikor aztán ROLLBACK TO sajat_pont; parancsot adsz ki, nem az egész tranzakció áll vissza, hanem csak az e pont után végrehajtott változások.
Martin: Ez olyan, mint a mentés a videójátékokban! Ha hibázok, nem kell teljesen az elejétől kezdenem, hanem csak az utolsó mentési ponttól.
Kristýna: Ez abszolút tökéletes analógia! Pontosan így működik. De fontos tudni, hogy a savepoint-ra történő ROLLBACK nem szabadítja fel az úgynevezett naplókat, azaz a log fájlokat. Csak a teljes ROLLBACK tisztítja meg őket.
Martin: Említetted a naplókat. Azok azok a fájlok, ahova az adatbázis felírja, mi történik, egy esetleges leállás esetére?
Kristýna: Igen. A napló, vagyis log, abszolút kulcsfontosságú a helyreállításhoz. Lényegében egy napló, ahova az adatbázis gondosan feljegyzi minden tranzakció minden lépését: mikor kezdődött, milyen adatokat változtat – régi és új értékeket is –, és mikor fejeződött be, akár COMMIT, akár ABORT paranccsal.
Martin: Szóval amikor összeomlik a szerver, aztán újraindítják, az első dolog, amit csinál, az, hogy elolvassa ezt a naplót?
Kristýna: Pontosan így van. Belenéz, és kideríti, mely tranzakciók voltak folyamatban az összeomlás pillanatában. Ehhez az úgynevezett UNDO/REDO algoritmust használja. Két listát készít: egy UNDO listát és egy REDO listát.
Martin: Mi van azokon a listákon?
Kristýna: A REDO listára kerül minden olyan tranzakció, ami még az összeomlás előtt sikeresen COMMIT-elt, de a változásai talán még nem íródtak ki fizikailag a lemezre. Ezeket a tranzakciókat a rendszer újra végrehajtja (REDO), hogy biztos legyen benne, az adatok teljesek.
Martin: És az UNDO listára?
Kristýna: Oda kerül minden olyan tranzakció, ami az összeomlás pillanatában nem volt befejezve. Ezeknél a rendszer fogja az összes végrehajtott változásukat, és egyesével visszavonja (UNDO). Ezzel biztosítja, hogy ne maradjon félkész, inkonzisztens munka az adatbázisban.
Martin: Szóval a napló segítségével a rendszer egy katasztrófa után rendbe tudja tenni az adatbázist, és vissza tudja állítani az utolsó ismert helyes állapotába. Ez lenyűgöző.
Kristýna: Ez az adatbázisok megbízhatóságának alapköve. Enélkül egyszerűen nem működne.
Martin: Hú, Kristýna, ez rengeteg információ volt. Próbáljuk meg a végén átfutni. A tranzakció olyan műveletek csoportja, ami egy egységként hajtódik végre – vagy minden, vagy semmi. Ezt hívják atomicitásnak.
Kristýna: Így van. Továbbá izoláltnak kell lennie, hogy a párhuzamos tranzakciók ne zavarják egymást. Az eredményeinek a megerősítés (COMMIT) után tartósnak kell lenniük, és a párhuzamos feldolgozás eredményének meg kell felelnie valamilyen soros végrehajtásnak.
Martin: Elosztott adatbázisoknál vannak globális tranzakciók, amik a lokálisakat irányítják kétfázisú commit segítségével. És hogy a tranzakciók ne nyúljanak bele ugyanazokba az adatokba, zárakat használnak.
Kristýna: Ami holtpontokhoz (deadlockhoz) vezethet, amiket a rendszer vagy detektál és megold, vagy megpróbál elkerülni időbélyegek segítségével.
Martin: Az SQL-ben pedig mindent főleg a COMMIT parancsokkal irányítunk a végleges mentéshez, és a ROLLBACK-kel a változások visszavonásához. És katasztrófa esetére az adatbázisnak van egy naplója, ami alapján helyre tud állni.
Kristýna: Tökéletes összefoglaló! Egyest kaptál rá. Szerintem most már mindenkinek világos, miért is nélkülözhetetlenek a tranzakciók.
Martin: Abszolút. Nagyon köszönöm, Kristýna, a szuper magyarázatot. És köszönjük nektek is, hogy hallgattátok a StudyFi Podcastot. A következő részben találkozunk!
Kristýna: Találkozunk!