Transakce

Transkript

Transakce
Transakce
DBI016
Marek Procházka
15/5/2000
Upozornění:
Tento text slouží jako učební pomůcka pro přednášku Transakce na Matematicko-fyzikální
fakultě Univerzity Karlovy. Jde o velmi předběžnou verzi, takže text není jazykově upraven
ani odborně konzultován. Zvláště se omlouvám za špatně přeložené nebo nepřeložené
odborné termíny – často české ekvivalenty zatím neexistují, nebo s nimi nesouhlasím. Budu
velmi vděčný za jakékoliv komentáře.
Poděkování:
Děkuji Ing. Petru Tůmovi, Dr. za pomoc s přípravou přednášky zejména v prvním roce jejího
konání (letní semestr ve školním roce 1997/1998). Původní texty Petra Tůmy jsou základem
kapitol o transakčních modelech, concurrency control, zamykání, sériových rozvrzích a
jiných.
Mgr. Marek Procházka
Katedra softwarového inženýrství
Matematicko-fyzikální fakulta
Univerzita Karlova
Malostranské náměstí 25
110 00 Praha 1
[email protected]
http://nenya.ms.mff.cuni.cz/~prochazk
1
2
Obsah
1.
ÚVOD ..............................................................................................................................10
2.
FAULT TOLERANCE ..................................................................................................10
2.1 ZÁKLADNÍ DEFINICE ......................................................................................................10
2.1.1
Dostupnost systému ..............................................................................................10
2.1.2
Spolehlivost a dostupnost modulu ........................................................................11
2.1.3
Výpadky ................................................................................................................11
2.2 BOJ PROTI CHYBÁM .......................................................................................................12
3.
2.2.1
Hardwarové metody pro odolnost proti chybám..................................................12
2.2.2
Softwarové metody pro odolnost proti chybám ....................................................12
TRANSAKČNÍ MODELY ............................................................................................13
3.1 SPHERES OF CONTROL ...................................................................................................13
3.2 NOTACE .........................................................................................................................14
3.3 FLAT TRANSAKCE ..........................................................................................................15
3.4 FLAT TRANSAKCE SE SAVEPOINTY .................................................................................16
3.5 ZŘETĚZENÉ TRANSAKCE ................................................................................................18
3.6 NESTED TRANSAKCE ......................................................................................................20
3.7 DISTRIBUOVANÉ TRANSAKCE ........................................................................................24
3.8 MULTI-LEVEL TRANSAKCE ............................................................................................24
3.9 OPEN NESTED TRANSACTIONS ......................................................................................25
3.10
DLOUHOTRVAJÍCÍ TRANSAKCE ..................................................................................25
3.10.1 Transakční kontext................................................................................................26
3
3.10.2 Mini-Batch ............................................................................................................28
3.11
SÁGY .........................................................................................................................30
3.12
EXOTIKA....................................................................................................................30
3.12.1 Kooperující transakce...........................................................................................30
3.12.2 Split transakce ......................................................................................................31
3.12.3 Joint transakce......................................................................................................32
3.12.4 RCA.......................................................................................................................32
3.12.5 ConTract model ....................................................................................................32
3.12.6 Unifikovaný transakční model ..............................................................................32
3.13
4.
HISTORICKÉ POZNÁMKY ............................................................................................33
TP SYSTÉMY.................................................................................................................34
4.1 POHLED KONCOVÉHO UŽIVATELE NA TP-SYSTÉM .........................................................34
4.2 POHLED SPRÁVCE TP-SYSTÉMU .....................................................................................34
4.2.1
TPC.......................................................................................................................35
4.3 POHLED NÁVRHÁŘE NA TP-SYSTÉM ..............................................................................36
4.4 POHLED RESOURCE MANAGERA NA TP-SYSTÉM ............................................................36
4.5 TRANSAKČNĚ-ORIENTOVANÝ VÝPOČETNÍ STYL.............................................................36
4.6 TAXONOMIE SPOUŠTĚNÍ TRANSAKCÍ ..............................................................................37
4.7 SLUŽBY TRANSAKČNÍHO SYSTÉMU ................................................................................38
4.8 PROCESY TP SYSTÉMU A JEJICH STRUKTURA .................................................................38
5.
TRANSAKČNÍ MONITORY .......................................................................................39
5.1 KOMPONENTY TRANSAKČNÍHO MONITORU ....................................................................40
4
5.2 TRANSAKČNÍ RPC .........................................................................................................41
5.3 INTERFACE RESOURCE MANAGERA ................................................................................42
5.3.1
Resource Manager Calls versus Resource Manager Sessions.............................43
5.4 FUNKČNÍ PRINCIP TP MONITORU ...................................................................................44
5.5 TRANSAKČNÍ FRONTY ZPRÁV .........................................................................................44
5.5.1
Volatile fronty .......................................................................................................44
5.5.2
Durable fronty ......................................................................................................44
5.6 DALŠÍ ÚLOHY TP MONITORU .........................................................................................45
6.
5.6.1
Vyvažování zátěže .................................................................................................45
5.6.2
Autentikace, autorizace ........................................................................................46
5.6.3
Zotavení z chyb .....................................................................................................46
CONCURRENCY CONTROL .....................................................................................46
6.1 ZÁVISLOSTI ...................................................................................................................47
6.2 FORMÁLNĚJI ..................................................................................................................47
7.
6.2.1
Wormholes ............................................................................................................49
6.2.2
Locking .................................................................................................................49
6.2.3
Converse Locking .................................................................................................50
UNIFIKOVANÁ TEORIE PRO CONCURRENCY A RECOVERY.......................50
7.1 TRADIČNÍ POHLED JINÝMI SLOVY ..................................................................................50
7.2 STRUČNÝ ÚVOD DO SEMANTICS-BASED CONCURRENCY CONTROL .................................51
7.3 UNIFIKOVANÝ MODEL ...................................................................................................52
7.3.1
Operace ................................................................................................................52
5
7.3.2
Komutativita .........................................................................................................53
7.3.3
Rozvrhy .................................................................................................................54
7.3.4
Rozšířené rozvrhy .................................................................................................54
7.3.5
RED a PRED ........................................................................................................55
7.4 UNIFIKOVANÝ MODEL PRO READ/WRITE OPERACE....................................................55
7.4.1
Vztahy ke klasické teorii .......................................................................................55
7.4.2
PRED = SOT ........................................................................................................56
7.5 UNIFIKOVANÝ MODEL PRO SÉMANTICKY BOHATÉ OPERACE ..........................................57
7.5.1
Reducibilní rozvrhy...............................................................................................57
7.5.2
PRED ≠ SOT.........................................................................................................58
7.5.3
Bezpečné rozvrhy..................................................................................................59
7.5.4
Perfektní komutativita...........................................................................................60
7.6 SHRNUTÍ ........................................................................................................................60
8.
ZÁMKY...........................................................................................................................60
8.1 STUPNĚ IZOLOVANOSTI..................................................................................................60
8.1.1
Jak je to v SQL?....................................................................................................61
8.2 FANTOMY A PREDIKÁTOVÉ ZÁMKY ................................................................................62
8.3 ZÁMKY S RŮZNOU GRANULARITOU ...............................................................................63
8.4 ZAMYKÁNÍ ROZSAHU KLÍČŮ ..........................................................................................64
8.5 DAG ZÁMKY .................................................................................................................65
8.6 HEURISTIKY...................................................................................................................66
8.6.1
Konverze zámků....................................................................................................66
6
8.6.2
Eskalace zámků.....................................................................................................66
8.7 ZAMYKÁNÍ V NESTED TRANSAKCÍCH .............................................................................67
8.8 PLÁNOVÁNÍ A DEADLOCK ..............................................................................................68
8.8.1
Konvoj...................................................................................................................68
8.8.2
Předcházení deadlocků versus jejich tolerance....................................................68
8.8.3
Wait-for graf a detekce deadlocku........................................................................69
8.8.4
Distribuovaný deadlock........................................................................................70
8.8.5
Pravděpodobnost deadlocku.................................................................................71
8.9 EXOTIKA........................................................................................................................71
9.
8.9.1
Field Calls ............................................................................................................72
8.9.2
Vylepšení Field Calls............................................................................................72
8.9.3
Optimistické a timestamp zamykání .....................................................................73
8.9.4
Time Domain Addressing .....................................................................................73
LOG MANAGER ...........................................................................................................74
9.1 LOGOVACÍ TABULKY .....................................................................................................74
9.2 PUBLIC INTERFACE K LOGU............................................................................................75
1.3 IMPLEMENTAČNÍ DETAILY .............................................................................................75
10. TRANSAKČNÍ MANAGER .........................................................................................76
10.1.1 Interface................................................................................................................76
10.2
FUNKCE TRANSAKČNÍHO MANAGERA ........................................................................76
10.3
KONCEPTY TRANSAKČNÍHO MANAGERA ....................................................................76
10.3.1 DO-UNDO-REDO protokol .................................................................................76
7
10.3.2 Idempotence versus testování ...............................................................................77
10.3.3 Fyzický log............................................................................................................78
10.3.4 Logický log ...........................................................................................................78
10.3.5 Fyzicko-logický log...............................................................................................78
10.3.6 Jednobitový resource manager.............................................................................78
10.3.7 FIX, WAL, Force-log-at-commit ..........................................................................78
DVOUFÁZOVÝ COMMIT PROTOKOL ............................................................................79
10.4
10.4.1 Persistence objektů a dvoufázový commit ............................................................79
10.5
CHECKPOINTING ........................................................................................................79
1.1.1
Sharp checkpointy.................................................................................................80
10.5.2 Fuzzy checkpointy.................................................................................................80
10.6
RESTART....................................................................................................................80
10.7
DALŠÍ VLASTNOSTI TRANSAKČNÍHO MANAGERA .......................................................83
10.7.1 Heterogenní commit koordinátoři ........................................................................83
10.7.2 Dobře dostupní (neblokující) koordinátoři commitu............................................83
10.7.3 Heuristická rozhodování během commitu ............................................................83
10.7.4 Přenos commitu ....................................................................................................84
10.7.5 Optimalizace dvoufázového commitu ...................................................................84
11. STANDARDY, SPECIFIKACE A IMPLEMENTACE .............................................84
11.1
OSI-TP, OSI-CCR, X/OPEN DTP.............................................................................84
11.2
TRANSAKCE V JAZYCE JAVA......................................................................................87
1.1.1
TJava ....................................................................................................................87
8
1.1.2
PJama ...................................................................................................................88
1.1.3
Java Transaction API (JTA), Java Transaction Service (JTS).............................96
1.1.4
Enterprise Java Beans ..........................................................................................96
1.2 TRANSAKCE V SYSTÉMU CORBA: OBJECT TRANSACTION SERVICE (OTS)..................98
1.3 TRANSAKCE V COM/DCOM: MICROSOFT TRANSACTION SERVER.............................101
2.
POUŽITÁ LITERATURA ..........................................................................................103
9
1. Úvod
Transakce - vágní pojem, řekněme že to je balík operací provedený nad nějakým stavem.
Transaction processing system - něco, co poskytuje nástroje pro vývoj a provoz aplikací, které
používají transakce.
Jenže to jsou všechno hrozně vágní termíny. Proto se přesněji definuje, že transakce je
operace, která je ACID, neboli která má následující čtyři vlastnosti:
−
−
−
−
Atomic: Stane se buď všechno nebo nic.
Consistent: Transakce nepoškodí data (tj. provede konzistentní update).
Isolated: Současně vykonávané transakce jsou serializovatelné.
Durable: Výsledek transakce je trvalý.
A jak tyhle vlastnosti zaručit je na transakcích ta největší věda :-) Podrobněji se k ACID
vlastnostem vrátíme v kapitole 3.3.
Co bychom chtěli po TP systému:
−
−
−
−
−
−
application development support (jasně, dneska už se neprogramuje, ale kliká),
repository features (repository popisuje konfiguraci systému),
transaction monitors (entity, které se starají o průběh transakcí nad servery),
communication features (pro distribuci, zabezpečení a podobně),
database features (podpora pro práci s velkými objemy dat),
operations features (podpora pro administraci běžícího systému).
Speciálně se zaměříme na distribuované transakce, kde jsou jednotlivé operace v rámci téže
transakce prováděny v různých databázích (nebo jiných resource managerech), případně na
různých uzlech počítačové sítě. Typickým příkladem distribuované transakce se v poslední
době stalo obchodování na internetu.
2. Fault Tolerance
Fault tolerance se dá do češtiny přeložit jako odolnost proti chybám. Zajistit odolnost
systému proti chybám je jedním z hlavních cílů používání transakcí.
2.1 Základní definice
2.1.1 Dostupnost systému
Dostupnost systému (system availability)
− je podíl času, kdy má systém přijatelnou dobu odezvy, k celkovému nabízenému času,
− měří se v procentech,
10
− 80-tá léta: 99% (to odpovídá 100 minutám výpadku každý týden), nyní (tj. rok 1993)
rozdělení na třídy 1-7, třída 1 je 90%, třída 7 je 99.99999%, kupodivu monitoring
jaderného reaktoru je třída 5.
Modul (softwarový či hardwarový) má specifikované chování (specified behavior) a zjištěné
chování (observed behavior). Výpadek či selhání (failure) je způsoben chybou v kódu (error),
která je způsobena pochybením programátora nebo nějakého systému (fault). Zpoždění chyby
(error latency) je doba mezi výskytem chyby v modulu a jejím projevením (failure). Chyba je
latentní (latent), pokud se ještě neprojevila. Chyba je efektivní (effective), pokud už se
projevila.
2.1.2 Spolehlivost a dostupnost modulu
Spolehlivost modulu (module reliability) charakterizují dvě hodnoty:
− MTTF (mean time to failure): čas do příští chyby,
− MTTR (mean time to repair): čas zotavení.
Dostupnost modulu (module availability) charakterizuje hodnota
MTTF/(MTTF + MTTR)
Chyby jsou soft, když se systém z chyby zotaví. Chyby jsou hard, pokud se z nich systém
nezotaví.
Failfast modul se zastaví po detekci chyby a má tudíž malou error latency. Neboli: je lepší,
když systém spadne, než aby běžel s chybami.
Typické MTTF:
− konektory, kabely 1000y MTTF,
− logické obvody 3-20y MTTF soft/hard=1/1 až 5/1 až 10/1,
− disky u PC 1y MTTF, dražší 5-20y MTTF (ale hodně záleží na typu chyb, soft read error
je jednou za hodinu, některé chyby jednou za milion let),
− workstations 3-5y MTTF, software 1w MTTF,
− SW: 3 chyby ve 100 řádcích kódu, 100/1 soft/hard,
− datové spoje v USA 10-9 BER (bit error rate) – optika,
− LAN: většina chyb kvůli protokolům, 3w MTTF.
2.1.3 Výpadky
Výpadky (outages) jsou takové situace, že je celý systém na nějakou dobu mimo provoz. V
nedávných dobách byly výpadky způsobeny zejména hardwarem, v poslední době se ale
spolehlivost hardware rapidně zvýšila. Výpadky můžou být způsobeny softwarovou chybou,
ale i jinými okolnostmi:
− 1990: požár na switchi odpojil na pár dnů od telefonů většinu Chicaga,
11
− 80-tá léta: americká burza měla 4 výpadky, na den kvůli sněhové bouři, na 4 dny kvůli
požáru v místnosti s počítači, nebo zavřela o 45 sekund dříve kvůli softwarové chybě a na
burze vznikla panika.
− 90-léta: výpadky kvůli atakům na Internetu.
Celkově vzato:
− Výpadky jsou celkem vzácné.
− Málokdy jsou výpadky způsobené hardwarem - většina chyb se maskuje.
− Pokud se už vyskytne výpadek kvůli hardware, pak je většinou doprovázen softwarovou
chybou (software by měl chybu maskovat), chybou operátora (nereagoval na chybu
správně) nebo chybou údržby (nejsou připraveny náhradní součástky, disky, mirror ap.),
− Podíl softwaru se zvyšuje, podíl výpadků způsobených softwarem ani moc ne.
Je zajímavé, že výpadky proudu jsou stále (v roce 1993) relativně velkým problémem. Např. v
USA tvoří 75% všech výpadků.
2.2 Boj proti chybám
Proti chybám je možné bojovat dvojím způsobem:
− Předcházení: Valid construction. To znamená vývoj software i hardware bez chyb,
případně minimalizace chyb.
− Zotavení při výskytu: Error correction. Zde rozlišujeme opravu latentních chyb (latent
error processing) a nebo opravu efektivních chyb (effective error processing) - tady jde
někdy o opravu a jindy o pouhé maskování chyby.
2.2.1 Hardwarové metody pro odolnost proti chybám
− Zdvojování (duplexing): Součástka či zařízení se zapojí dvakrát a porovnávají se výstupy.
Pokud výstupy nesouhlasí, systém se zastaví. Je problém, že výstupy nemusí být vše
říkající (chyba se nemusí na výstupech projevit ihned). Další věc je, že tento přístup sice
snižuje možnost vadného chování nějakého zařízení, ale pravděpodobnost, že aspoň jedno
zařízení je vadné, je vyšší než pro jedno zařízení.
− N-plexing: Podobné jako duplexing, ale volí se takový výsledek, pro který hlasuje většina
zapojených zařízení. Toto lze provádět rekurzivně.
− Obvyklé je TMR (triple module redundancy).
− Hlasování bývá vylepšené tím, že hlasují jen dostupné moduly.
2.2.2 Softwarové metody pro odolnost proti chybám
Už jsme si řekli, že hlavním problémem je software. Zde jsou metody, jak se dá na softwarové
úrovni bojovat proti chybám:
− Metody návrhu: Strukturované programování, inspekce kódu, zajištění kvality (quality
assurance, ISO 9001 ap.), kvalitní specifikace, nástroje, management, lidé. Např.
americké vesmírné programy mají až $5000 na jeden řádek kódu.
12
− Redundantní programování: Funguje podobně jako u hardware - program se napíše
vícekrát a porovnávají se výsledky. To je samozřejmě finančně dost náročné.
− Defenzívní programování: Počítá se se všemi eventualitami, počítá se s tím, že uživatel
může chtít systém shodit, že ten, kdo bude používat nějakou knihovnu, ji může používat
nesprávně ap.
− Transakce.
3. Transakční modely
Nejdříve je užitečné klasifikovat akce, ze kterých se transakce staví. To budou:
− Unprotected actions: Obyčejné operace, které nezaručují nic krom konzistence
(kdybychom nechtěli ani konzistenci, nedalo by se programovat).
− Protected actions: To budou právě naše ACID akce, neboli transakce.
− Real actions: Akce, které mají nějaký reálný efekt, tedy jejich efekty obvykle nelze vracet
(neboli unprotected actions bez možnosti UNDO).
Unprotected actions se balí do protected actions. Real actions se také balí, ale krom toho se
jim věnuje zvláštní pozornost, např. se dá přesunout jejich vykonání těsně před commit, aby je
nebylo potřeba vracet kvůli jiným akcím.
Většina transakčních modelů vznikla jako ad-hoc řešení nějakého konkrétního
programovacího problému. Je zajímavé zkoumat jednotlivé nápady rozšiřující původní
koncept transakcí a hledat společné rysy transakčních modelů, které by se daly zobecnit. O to
se snaží například systém ASSET [11].
3.1 Spheres of Control
První snahy o formalizaci něčeho, čemu se později začalo říkat transakce, byly na začátku 70tých let. Z toho vypadl v roce 1978 ([6]) koncept spheres of control. Řízení výpočtu znamená:
− držení efektů dílčích operací, dokud je možnost je zrušit,
− monitoring eventuálních závislostí mezi operacemi pro případ zpětného trasování chyb v
historii výpočtu.
Původní myšlenka byla, že operace v rámci výpočtu budou nad hierarchií abstraktních
datových typů (ADT). Částečné změny na hierarchii by nebyly externalizovány, vždy by byly
externalizovány celé hierarchie. Myslelo se, že tím se to celé usnadní, protože se rozumně
omezí granularita.
Jenže už jednoduchý debit/credit příklad je o něčem jiném: pracuje se s dvěma úplně jinými
ADT, kde celkový commit není realistický. Definuje se:
− Process control: Řízení celého procesu, chránění informací o závislostech a různých
omezeních.
− Process atomicity: Určitá část výpočtu se identifikuje jako atomická.
− Process commitment: Během výpočtu nejsou pro ostatní výpočty viditelné výsledky,
existuje tedy jediné místo, kam je nutné se při případném trasování chyby vracet.
13
Rozdíl od transakcí je ten, že commit nějakých dat může být mimo proces, který je
vyprodukoval. Dynamicky se vytvoří nová sphere of control (SoC), která sleduje závislosti a
hostorii výpočtu. Když nějaká funkce uzavřená do SoC pošle data jiné funkci uvnitř SoC, pak
se obě tyto funkce zabalí do nové SoC. Commit je jen místo, kdy se exportují data (aby v tom
nebyl zas tak velký binec).
Takže: sledují se všechny závislosti všech verzí všech dat libovolně zpět do historie. Při
složitějších závislostech se může pokračovat v zabalování i směrem dopředu! (Když je nějaký
proces, který ještě běží, závislý na procesu, který jsem trasoval zpětně kvůli chybě).
Je to hodně nerealistické? Uvědomme si, že existují organizace, kde by to šlo, všechny
dokumenty a operace jsou zpětně dohledatelné. Protipříklad: vrácení neoprávněného vybrání
peněz z bankomatu. Jenže tohle neumí řešit ani ostatní modely, se kterými se seznámíme a
které se tváří jako zcela realistické! Buď jak buď, koncept SoC se neujal a je skutečně obtížně
realizovatelný (ani nebyl důkladně formalizován).
V 90-tých letech se k odkazu SoC hlásí autoři nových transakčních modelů (advanced
transaction models). V těchto nových transakčních modelech se oprášila myšlenka spolupráce
mezi transakcemi a vytváření běhových závislostí mezi transakcemi.
3.2 Notace
Měli bychom se seznámit s notacemi zápisu transakčních modelů.
1. Pravidla podle [1]:
<rule id>:<rule precondition> à <rule modifier list>, <signal list>, <state transition>
<rule modifier> :== +(<rule identifier>,<signal>)
2. Grafická notace podle [1]: Obdélníček se vstupy signálů, identifikací a výstupy.
Obrázek 1: Grafická notace zápisu transakčních modelů
Obrázky požívající grafickou notaci jsou zkopírovány z [1].
3. Pravidla podle ACTA od Ramamrithana a Chrysanthise [7].
První dvě jsem neviděl nikde jinde než v [1], přitom je tato kniha citovaná ve většině jiných
knih, specifikací nebo článků, takže se diagramy a pravidla zřejmě příliš neujaly. Proto si je
14
ukážeme jen na nejjednodušších příkladech, ale nebudeme je používat pro komplikovanější
modely.
ACTA se v literatuře o transakčních modelech citují velmi často. ACTA pravidla se dají
použít pro poměrně přesnou formální specifikaci transakčních modelů, ze které je patrné, jak
jednotlivé transakční modely fungují. Protože ale k použití ACTA potřebujeme další
vědomosti, se kterými se seznámíme až v následujících kapitolách (zejména týkající se
serializovatelných rozvrhů), vrátíme se k ACTA později v kapitole Error! Reference source
not found..
3.3 Flat transakce
Tento transakční model reprezentuje základní, nejjednodušší transakce, s operacemi begin,
commit a abort, bez žádné vnitřní struktury. Flat transakce mají jen základní ACID vlastnosti:
− Atomic: Akce se buď provede celá, nebo se to řízení programu vrátí zpět, jako by se nic
neprovedlo. Účinky akce jsou vidět až je bezpečně ukončena, neodešlou se žádné zprávy,
tedy do důsledku vzato ani zprávy o chybě.
− Consistent: Výsledkem akce je konzistentní stav. To se dá těžko zařídit, spíš se to
používá tak, že konzistentní stav je takový, který je výsledkem nějaké dokončené
transakce, která začala v jiném konzistentním stavu.
− Isolated: Akce běží nezávisle na jiných akcích. Akce o sobě vzájemně nevědí. V
databázích to bývá většinou zařízeno použitím zámků – takže transakce nevidí změny
určitého záznamu, protože ten je zamknutý do ukončení transakce, která ho modifikovala.
Takže se transakce přece jen dozví o jiné transakci – tím, že je určitou dobu zablokována.
Pokud transakce vytvoří nový záznam nebo novou tabulku, pro ostatní transakce tento
nově vytvořený záznam nebo tabulka neexistují, dokud není transakce, která je vytvořila,
commitována.
− Durable: Výsledky jsou trvalé, neboli přežijí rozumné katastrofy. Většnou se tím myslí
uložení do persistent store, neboli uložení do paměti, kterou pokládáme za trvalou (třeba
do souboru, do databáze).
Co nám vadí na flat transakcích nejvíc?
− Mají příliš velký rozsah. Abortu po stotisícé operaci se nedá nijak zabránit. To se dá
přičíst tomu, že nemají žádnou vnitřní strukturu.
− Žádný dílčí neúspěch není tolerován. Pokud je špatně jediné číslo konta u deseti tisíc
zaměstnanců nějakého podniku, pak transakce odeslání mezd všem zaměstnancům kvůli
této chybě abortuje.
− Flat transakce nemohou spolupracovat s jinými transakcemi. Nemohou s nimi sdílet data
(pro sdílení dat neexistují žádná primitiva nebo pravidla), nemohou být vzájemně
propojeny přes signifikantní události.
− Neřeší přirozený problém vnoření do sebe. Běžné programovací jazyky umožňují
vnořovat volání procedur, takže se může stát, že zavoláme v transakci proceduru, která
zahájí novou – vnořenou – transakci.
Obrázky: Žádná spojení, kromě propojení abortu systémové transakce do abort vstupu
transakce.
15
Obrázek 2: Flat transakce
Pravidla:
BEG(T) à (DEL(BEG(T)), +(ABT(SYS)), ABT(T)), begin work
ABT(T) à (DEL(COM(T)), rollback work
CMT(T) à (DEL(ABT(T)), commit work
3.4 Flat transakce se savepointy
První situací, která zjevně demonstruje omezení flat transakcí, je rollback při 10000-té
operaci. Nejjednoduší nápad, jak tohle opravit, je dovolit rollback jinam, než jenom k začátku
transakce (částečný rollback). Proto se zavádějí savepoints (SP), neboli při rollbacku se řekne,
ke kterému SP se vrací výpočet.
Podrobněji:
− Rollback k nějakému SP neukončuje celou transakci.
− Obvykle se vytváří první SP ihned po začátku transakce, aby bylo možno provést rollback
celého výpočtu v transakci bez skutečného abortu celé transakce (tj. zavádí se neviditelné
undo a retry).
− Je potřeba rozhodnout, zda se může dělat rollback k již rollbacknutým stavům (tzv.
dopředný rollback). Pokud by to nebylo možné, pak se jednou rollbacknutá historie
navždy zapomene. V opačném případě se pamatují všechny částečné rollbacky, které se
pak stávají součástí historie výpočtu (tj. rollback k savepointu, který je před nějakým
rollbackem k SP, vede na anulaci tohoto rollbacku).
Obrázky: Transakce má tolik krabiček, kolik je savepointů, plus systémová a celková
transakce. Systémový abort (nějaká hard chyba systému) vede na abort S1, atd. Commit SN
způsobí commit SN-1, … až commit S2 způsobí commit S1.
16
Obrázek 3: Flat transakce se savepointy
Pravidla (delete se vynechává):
BEG(S1) à +(ABT(SYS), ABT(S1)), begin work
ABT(S1) à rollback work
CMT(S1) à commit work
SAV(S1) à +(ABT(S1), ABT(S2)), BEG(S2)
. . .
BEG(SN) à begin work
ABT(SN) à ABT(SN+1), rollback work
CMT(SN) à CMT(SN-1), commit work
SAV(SN) à +(ABT(SN, ABT(SN+1)), BEG(SN+1)
Celé snažení s částečnými rollbacky je ale k ničemu, uvědomíme-li si, že může nastat tvrdý
výpadek systému a celá transakce musí být zopakována znova. Savepointy by tedy musely být
persistentní, aby se rollback provedl jen k poslednímu z nich. To by znamenalo porušení
atomicity (např. výpočty úroků na všech účtech by neproběhly “najednou”). Navíc je takřka
nemožné udržovat a pak obnovit stav výpočtu uprostřed transakce (představme si současné
programovací jazyky, řídící proměnné, kurzory, atd.).
17
Flat transakce se SP jsou podporovány např. databázovým systémem Oracle. V tomto
systému není možné se vracet k rollbacknutým savepointům. Např. spuštěním následujícího
skriptu obdržíme v tabulce tři řádky "aaa", "bbb" a "ddd":
savepoint A;
insert into msg values ('aaa');
declare
pragma AUTONOMOUS_TRANSACTION;
cnt number;
begin
insert into msg values ('bbb');
savepoint A;
insert into msg values ('ccc');
rollback to savepoint A;
insert into msg values ('ddd');
commit;
end;
3.5 Zřetězené transakce
Transakce s persistentními savepointy se ukázaly těžko implementovatelné – při abortu
systémové transakce by bylo potřeba rekonstruovat stav výpočtu v posledním savepointu
(SP), což současné konveční programovací jazyky dost dobře neumožňují.
Proto se zavádějí zřetězené transakce (chained transactions): K flat transakcím se přidá nová
operace CHAIN WORK, která provede COMMIT WORK prováděné transakce a BEGIN WORK nové
transakce. Obě tyto akce provede samozřejmě atomicky. Z toho ihned plyne výhoda: nemusí
se zavírat SQL cursory mezi jednotlivými transakcemi, což může mít nezanedbatelný vliv na
výkon. Místo vytváření savepointu je možné atomicky commitnout transakci a zahájit novou
transakci.
Stále je ale potřeba uvažovat řešení situace, kdy je potřeba zbytečně provést znovu celou
transakci, když už je provedeno korektně 10000 operací. V případě abortu se provede rollback
pouze poslední (aktivní) transakce v řetězci transakcí za cenu toho, že je zjevně porušena
atomicita.
Obrázky: vcelku jednoduché, C nějaké transakce je spojeno s B následující transakce, po
ukončení transakce se přepojuje A systémové transakce na A probíhající transakce.
18
Obrázek 4: Zřetězené transakce
Pravidla:
BEG(CN) à +(ABT(S),ABT(CN)), begin work
ABT(CN) à rollback work
COM(CN) à BEG(CN+1), commit work
Srovnání zřetězených transakcí s transakcemi se SP:
− Workflow: Obojí transakce umožňují dlouhodobější práci složenou z více operací
sekvenčně řazených za sebou, kdy po první chybě není hned úplně všechna práce
ztracena. Kontext v případné databázi je chráněn, kurzory zůstávají otevřené.
− Commit a savepoint: Rollback může být proveden jen k poslední operaci commit, na
druhou stranu při rollback k nějakému SP si můžu vybrat, ke kterému SP rollback
provedu. Nabízí se možnost kombinovat zřetězené transakce se SP.
− Granularita zámků: Commit umožňuje uvolnit zámky, což může být důležité pro
celkový výkon. U transakcí s SP by totiž uvolnění zámku porušilo izolovanost transakce,
ale tady je proveden commit a o transakci se mohou prostřednictvím odemknutého zámku
dozvědět ostatní.
− Ztracená práce: Transakce s SP nechrání už udělanou práci před systémovým abortem.
Zřetězená transakce ano.
− Provedení restartu: Když se nad tím zamyslíme, je při restartu systému po systémovém
abortu stejný problém jaku u SP: Vzhledem k tomu, že jsou otevřené SQL kurzory ap., je
nutné rekonstruovat i stav výpočtu v posledním commitu. A jsme zase tam, kde jsme byli.
19
Nic se zkrátka neříká o tom, jak se provede rollback a jak se provede restart. Zůstává se při
tom, že se to děje uvnitř Sphere of Control. Obázek k restartu: k nedokončené transakci C se
vytvoří po restartu transakce C', která provede nedokončený zbytek transakce C a pak se
normálně zřetězí s dalšími transakcemi, které měly následovat po C. Otázka je, kde vzít
vstupy pro C'.
Obrázek 5: Restart zřetězené transakce
3.6 Nested transakce
Místo abychom se na nějaký výpočet dívali jako na posloupnost operací (transakcí), budeme
se na něj dívat jako na hierarchii operací (transakcí). Transakce v tomto modelu bude
obsahovat podtransakce, které budou mít vlastnosti A,C a I.
Definice:
1. Nested transakce je strom transakcí, jehož podstromy jsou nested transakce nebo flat
transakce.
2. Top-level transakce je transakce nejvyšší úrovně, tj. transakce, která není vnořená do
žádné jiné transakce. Někdy se také používá označení kořenová transakce (root
transaction).
3. Podtransakce (subtransaction) je transakce vnořená do jiné transakce. Podtransakce tedy
má rodičovskou transakci.
4. Rodičovskou (parent) transakcí nějaké transakce nazýváme transakci, do které je daná
transakce vnořená. Tuto vnořenou transakci pak nazýváme dceřinnou (child) transakcí.
Top-level transakce tedy nemá rodiče. Podtransakce má pouze jednu rodičovskou
transakci (existují obecnější modely s více rodičovskými transakcemi – multiparent
transactions).
5. Podtransakce může provést commit nebo abort. Commit nemá žádný efekt, dokud se
neprovede commit rodiče. Neboli: žádná transakce neprovede finální commit dokud
neudělá commit top-level transakce.
20
6. Rollback (pod)transakce způsobuje rollback všech jejich podtransakcí. To je důvodem,
proč podtransakce nemají vlastnost D.
Vnořené transakce vymyslel jistý pan Moss [8], ten ale ještě přidal podmínku, že jiné než
listové transakce pouze řídí běh výpočtu a veškeré výkonné operace (přístupy do DB ap.)
provádějí listy. Nyní se toto pravidlo nedodržuje. Naopak, ještě se hovoří o concurrent
transactions, tzn. jednotlivé podtransakce běží paralelně.
Pro práci s vnořenými transakcemi platí následující pravidla:
− Commit pravidlo: Commit podtransakce je viditelný pouze pro jejího rodiče.
Podtransakce může provést finální commit, pouze když všechny generace rodičů provedly
svůj commit. Speciálně to platí pro top-level transakci.
− Rollback pravidlo: Když transakce provede rollback, všechny její podtransakce až po
listy musí také provést rollback. Nezáleží na tom, že byl třeba některou podtransakcí
proveden částečný commit. Speciálně to platí pro top-level transakci – její abort znamená
abort celého stromu podtransakcí. Toto je moment, kdy je u podtransakcí porušena
vlastnost D.
− Pravidlo viditelnosti změn: Objekty viditelné transakcí mohou ale nemusí být
zpřístupněny podtransakcím. Změny provedené v nějaké transakci jsou viditelné
rodičovské transakci, ale až po provedení (částečného) commitu. Změny nejsou v
obecném případě viditelné sourozenci (leda že je zpřístupní rodičovská transakce poté, co
jsou jí zpřístupněny částečným commitem).
Obrázky: Celkem zřejmé. Pozor, commit se nepropaguje, ale podmiňuje se ukončenými
podtransakcemi!
21
Obrázek 6: Nested transakce
Pravidla:
BEG(SUB) à +(ABT(PAR),ABT(SUB)), begin work
ABT(SUB) à rollback work
COM(SUB):(COM(PAR)) à commit work
Pozor, nic se neříká o tom, že by všechny podtransakce nějaké transakce musely udělat
commit, aby i transakce udělala commit! Částečné aborty jsou tedy povolené. Přidání
takového pravidla by bylo triviální. Chybí rozlišení částečného a finálního commitu, možná
by to mělo vypadat spíš takhle:
BEG(SUB) à +(ABT(PAR),ABT(SUB)), begin work
ABT(SUB) à rollback work
FCOM(SUB):(COM(PAR)) à final commit work
COM(SUB) à commit work
Použití a shrnutí:
− Nejdůležitější vlastností vnořených transakcí je možnost provést částečný rollback
transakce. Rollback top-level transakce způsobí rollback všech podtransakcí.
22
− Vnořené transakce jsou podobné modularizaci v softwarovém inženýrství. Několik
pokusů o implementaci tuto vlastnost využívající: Argus (1988), Camelot (1991) –
prototyp, ze kterého později vzikl Encina TP Monitor s jazykem Transactional C (nyní
pod IBM).
− Vysvětlení podobnosti modularizace a nested transakcí na příkladě. Dobře definované
moduly mají dobře definovaný interface, který je jedinou styčnou plochou s okolím.
Nejsou-li použity globální proměnné, nemohou existovat žádné nežádoucí side-efekty,
takže při chybě modulu se vlastně navenek nic nestane (stejně jako při transakci). Z toho
plyne jistá komplementárnost modulárního software a transakčního zpracování: modulární
design chrání lokální (dynamická) data a transakce chrání při stejné granularitě globální
data použitá danými moduly.
− Přesto s výjimkou Transactional C téměř neexistuje systém, který by dával možnost
obecně využívat vlastnosti nested transakcí. Pozor – to platí pro rok 1993, kdy byla
napsána kniha, ze které jsem čerpal, nyní se tváří mít nested transakce některé
implementace OTS od OMG, Jaguar CTS od Sybase, ap.
− Je s podivem, že ani RDBMS nevyužívají nested transakce, ačkoliv SQL má k tomuto
stylu práce velice blízko. SQL update provádí commit pro transakci vyššího řádu, jinak se
vrátí všechna práce zpět. SQL výrazy mají všechny vlastnosti podtransakcí, ale zatím se
dává přednost tomu, že si to každý ad-hoc naprogramuje sám, místo aby se připravil
obecný mechanismus, který by to řešil.
Emulace nested transakcí transakcemi se SP:
− Na obrázku (obrázek si každý domyslí :-) je vidět, že při emulaci je dokonce možné se
vracet k již hotovým savepointům, což nested transakce nedovolují.
− Potíže by nastaly při concurrent transactions – ty emulovat nelze. Nelze totiž provést
rollback k nějakému dřívějšímu SP, aniž bychom implicitně neprovedli rollback k SP,
které následovaly po něm (s vyššími identifikačními čísly).
− Další potíže jsou se zámky. Rodič může v nested transakcích rozhodovat, jestli dá svým
potomkům přístup k zámku, naopak děti mohou rodičovské transakci po svém commitu
zpřístupnit jimi vytvořené zámky. Při transakcích se SP tohle nejde – jde vlastně o jedinou
flat transakci, během které jsou všem přístupné všechny zámky.
V literatuře se někdy sémantika vnořených transakcí popisuje poněkud odlišným způsobem.
O částečných commitech se prohlásí, že nedělají nic jiného, než že delegují log se všemi
záznamy o provedených operacích rodičovské transakci. Top-level transakce pak nesmí
commitovat dříve, než obdrží logy od všech svých podtransakcí. O tomto vztahu se říká, že
rodičovská transakce je commit-závislá na dceřinné transakci. Fakt, že abort rodičovské
transakce způsobí abort všech dceřinných transakcí, se označuje je abort-závislost dceřinné
transakce na rodičovské transakci. Ve formalizmu ACTA [7] se definuje více různých druhů
závislostí a přesněji se popisuje sémantika sdílení dat mezi podtransakcemi.
Oracle v některých materiálech píše, že podporuje nested transakce, ale není to tak úplně
pravda. Oracle totiž v okamžiku spuštění transakce suspenduje aktivní transakci (pokud
taková existuje). Žádné vazby mezi takto vnořenými transakcemi neexistují.
23
3.7 Distribuované transakce
Distribuovaná transakce není nic jiného než flat transakce běžící v distribuovaném prostředí.
Rozdíl od vnořených transakcí je ten, že dekompozice na podtransakce nezáleží na přirozené
modularitě vyplývající ze Spheres of Control, ale na tom, jak jsou data rozdistribuovaná po
síti.
Uvažujme příklad, kdy v určitém okamžiku je nutné pracovat s nějakými daty, které jsou
někde na síti. Výpočet se rozdělí a na více uzlech pokračuje výpočet. Neboli vznikne několik
podtransakcí, které ale dělají totéž a pokud některá provede commit, znamená to commit pro
rodičovskou transakci. Také jejich abort provede abort rodičovské transakce.
Přesnější sémantika může být i taková, že abort podtransakce nezpůsobí abort rodičovské
transakce. Zkrátka provádím nějaký výpočet pro jistotu na několika uzlech a nevadí mi, že
některé varianty se neprovedou.
3.8 Multi-Level transakce
Jde o verzi nested transakcí, která umožňuje pre-commit, takže některé zdroje nejsou zbytečně
zamknuty až do celkového commitu. Aby to mohlo fungovat, je přidána kompenzační
(compensating) transakce, která umí vrátit zpět efekty nějaké transakce.
Problém nastane v okamžiku, kdy kompenzační transakce provede abort. Řeší se to tak, že se
definitoricky řekne, že kompenzační transakce provede vždy commit. Navíc se doplní
"rozumně" podmínky nad daty, která transakce manipulují – totiž striktní hierarchie přístupu.
Příklad: SQL příkaz INSERT je top-level transakce, která se skládá z několika podtransakcí.
Jedna z nich je například fyzické vložení n-tice do nějaké stránky, jiná je změna souboru
indexů, ap. Každá z těchto podtransakcí by se dala dále rozčlenit na menší podtransakce –
změna souboru indexů znamená otevření souboru indexů, zápis nové položky a opětovné
zavření souboru indexů. Výhoda multi-level transakcí je v tom, že není nutné mít do
celkového commitu příkazu INSERT zamknutou příslušnou stránku, indexový soubor ap., ale
tyto jsou odemknuty ihned po vykonání příslušné podtransakce.
Tato hierarchie musí být dodržována, takže při implementaci různých view a browserů nějaké
databáze se používají jen operace z nejvyšší vrstvy, které pracují na úrovni celých n-tic
(SELECT, INSERT, UPDATE, …), nikdy nesmí být použity operace pracující přímo s objekty na
nižší vrstvě. (V tomto případě je to trošku přitažené za vlasy, protože by se těžko pracovalo s
hlavičkami stránek nebo indexovými soubory – to je však dáno dobře navrženými SQL
příkazy.)
Vrstva n je tedy implementována pomocí operací z vrstvy n-1, nikdy nepoužívá operace z
nižších vrstev. Chráněné jsou v nějaké vrstvě jen objekty, které implementují danou vrstvu, tj.
v našem příkladě na úrovni operace INSERT je chráněná jen nová n-tice (její klíč ap.), na
úrovni stránek je chráněný zápis do dané stránky, a podobně.
24
Pravidla: T je top-level transakce, N je její podtransakce, CN je kompenzační transakce k N.
Abort rodičovské transakce způsobí begin kompenzační transakce. Pravidla pro T jsou stejná
jako pro flat transakci. Další pravidla:
BEG(N) à begin work
ABT(N) à rollback work
COM(N) à +(ABT(T),BEG(C)), commit work
BEG(C) à +(COM(restart),BEG(CC)), begin work
ABT(C) à BEG(CC), rollback work
COM(C) à commit work
Vlastně se zdvojí kompenzační transakce – na commit transakce restart je nalinkována přes
begin port transakce CC, která bude spuštěna po restartu. Pokud dojde k abortu kompenzační
transakce, bude CC také spuštěna.
Výhody multilevel transakcí:
− Problémy v případě použití nested transakcí – některé zdroje jsou nepřístupné pro ostatní
transakce, protože nebyl proveden finální commit. Při použití multilevel transakcí se
např.v DBMS snadno najde inverzní operace ke každé operaci (INSERT vs. DELETE).
− Je potřeba vzít četnost rollbacků. Pak se možná ukáže, že občasné problémy s
kompenzační transakcí jsou vyváženy mnohonásobně vyšším výkonem, navíc k rollbacku
dochází velice zřídka.
− Mají vlastnosti A,C,I i D.
3.9 Open Nested Transactions
Verze multilevel transakcí, kde nejsou nikterak závislé commity dětí na commitech rodičů a
naopak. Jsou to v podstatě unprotected actions, ale na nějaké speciální případy se (prý) hodí.
Přívlastek "open" tady znamená "no closed sphere of control".
3.10 Dlouhotrvající transakce
Znovu se vraťme k problému, kvůli kterému jsme začali vylepšovat flat transakční model. Jde
o příklad, kdy máme najednou přičíst na deseti tisících účtů úroky. Jak se nám tento případ
podařilo řešit pomocí modelů transakcí popsaných v předcházejících kapitolách?
− Flat transakce dodržují sémantiku "only once", při restartu těsně před commitem systému
je ale zbytečně ztraceno velké množství udělané práce.
− U vnořených transakcí a transakcí se SP jsou ACID stejně jen celé transakce, takže při
restartu se vrací výpočet na začátek.
− Multi-level transakce jsou definovány tak, že abort rodičovské transakce způsobuje start
kompenzační transakce – tj. počítá se s tím, že když nemohou být úspěšně dokončeny
všechny podtransakce, nebude dokončena žádná.
25
− Zřetězené transakce vyhovují, protože při pádu systému se výpočet vrací do posledního
commitu. Je tu však problém s rekonstrukcí výpočtu, do kterého je potřeba se vrátit.
Stavové informace tento koncept neřeší (a neohlížely se na ně ani ostatní modely). Navíc
se jednotlivé změny na bankovních kontech projevují postupně (nikoliv najednou) – po
každém commitu.
V tomto kontextu hovoříme o dlouhotrvajících transakcích (long-lived transactions). (Pozor,
nejedná se o transakční model, ale o koncept, kdy se na transakce díváme z pohledu jejich
velké délky). Na transakce máme tedy následující podmínky:
1. Chceme vhodně strukturovat transakce složenou z více sekvenčně řazených operací.
2. Chceme, aby se automaticky udržovaly informace o tom, jak má výpočet dále pokračovat.
Řešením jsou různé formy log-souborů, ale běžné programovací jazyky nepodporují žádné
log-soubory. Speciálním případem dlouhotrvajících transakcí jsou transakce s otevřeným
koncem (open-ended transactions). Takové transakce nikdy neprovádějí commit, protože se
jedná o "nekonečné" nebo stále běžící výpočty. Příkladem takové transakce je běh operačního
systému, běh řídícího systému, ap. Transakce může skončit pouze abortem, jinak pořád běží.
3.10.1
Transakční kontext
Příklad:
SimpleProgram()
{
BEGIN WORK;
read(input_msg);
/* perform computation on input message */
. . .
send(output_msg);
COMMIT WORK;
};
Jde o program, kde ve vstupní zprávě je definováno, co se má udělat, udělá se to a ve výstupní
zprávě je výsledek. Výsledek je závislý jen na vstupu. Dá se napsat:
output_msg = f(input_msg).
Jiný příklad:
exec sql
declare cursor c as
exec sql
select a,b,c
from rel_a
where d = 10
order by ascending;
exec sql
open cursor c;
do
exec sql fetch c into :a,:b,:c;
{
/* perform computation */
26
exec
}
while (sqlcode == 0);
sql
close cursor c;
Je to příklad práce s databázovým serverem, na serveru je kontext operace (provádí se fetch
následujícího). Výsledek je závislý na vstupu i na kontextu. Dá se napsat:
f(input_msg,context) à (output_msg, context').
Existují context free a context sensitive programy. Co ale přidat do kontextu? Celou databázi,
stav všech konkurentních výpočtů,…? To bychom se daleko nedostali, zjistili bychom, že je
nakonec vlastně spojeno všechno se vším.
Operaci fetch popisují dva kontexty: kurzor – to je private context, a obsah databáze neboli
všechny ostatní transakce na ní pracující – global context. V našich úvahách se omezíme na
private context.
Vlastníkem kontextu může být např.:
− Transakce: to je v příkladu cursor c.
− Program: Například když se spustí několik transakcí, program ví, kolik jich provedlo
commit.
− Terminál: Údaje, kdo může s terminálem pracovat, jaké funkce se mohou na terminálu
spustit, poslední obrazovka, ap.
− Uživatel: Heslo, pořadové číslo, se kterým naposledy pracoval, ap.
Příklad: Otevření souboru – kontext má jednak program (deskriptor obsahuje spoustu údajů) a
jednak file system (id uživatele, práva, zámky, …).
Jiný příklad: Generátor náhodných čísel pracuje s kontextem posledně vygenerovaného čísla.
Příklad:
ComputeInterest()
{
read(interest_rate);
for (account_no = 1; account_no <= 10000; account_no++)
{ SingleAccount(interest_rate, account_no);
}
reply(“done”);
};
SingleAccount(interest_rate, account_no)
{
BEGIN WORK;
/* do account update */
COMMIT WORK;
Return(“OK”);
}
27
Celá transakce (tj. update všech účtů) není context-free – při pádu systému nesmí být
proveden update na stejné konto. Funkce SingleAccount ovšem je context free, závisí totiž
jen na vstupních parametrech. Tato funkce mění obsah trvalé paměti (v tomto případě účtů v
databázi), neboli DB si "pamatuje", jak daleko celá transakce došla (u kterého účtu skončila).
Naproti tomu má ComputeInterest kontext, ale nepříliš dobře chráněný (jen v lokální
proměnné).
Řešení je zřejmé: uchovávat kontext ve funkci ComputeInterest také v trvalé paměti.
Shrnutí:
− Kromě parametrů závisí průběh výpočtu ještě na něčem dalším – na kontextu výpočtu (ten
se může měnit při každém spuštěni programu).
− Kontext jsou veškeré čítače, stavy obrazovky, poslední přečtený záznam, poslední
změněný záznam. Programy, které něco čtou z trvalé paměti (a podle toho, co v ní je, ji
modifikují), jsou závislé na jejím kontextu.
− Kontext je durable (čítač v příkladu) nebo volatile (EOF).
Každá transakce má svůj transakční kontext, který zahrnuje všechno čeho se kdy transakce
dotkla. Někdy se transakčním kontextem myslí jen jakýsi identifikátor, který transakci
označuje. Je zbytečné asociovat transakci se všemi zámky, které kdy zamkla, když stačí znát
její identifikátor a u každého zámku mít identifikátor transakce, která ho drží.
3.10.2
Mini-Batch
Metoda rozdělení transakce složené z mnoha sekvenčně řazených operací na menší transakce
- dávky (batch), kontext výpočtu se ukládá do databáze.
Příklad:
Computelnterest (interest-rate)
{
long account-no,last-account-done, batch-date;
double account-total, interest-rate;
int logsize;
#define stepsize 1000;
#include <string.h>
#define max-account-no 9999;
logsize = 0;
exec sql SELECT COUNT(*) INTO :logsize FROM batchcontext;
if (SQLCODE!= 0 || logsize == 0)
{
exec sql BEGIN WORK;
exec sql DROP TABLE batchcontext;
exec sql CREATE TABLE batchcontext (last-account-done INTEGER);
28
last-account-done = 0;
exec sql INSERT INTO batchcontext VALUES(:last-account-done);
exec sql COMMIT WORK;
}
else
{
exec sql SELECT last-account-done
INTO: last-account-done
FROM batchcontext;
}
while (last-account-done < max-account-no) /* loop over all accounts */
{
exec sql BEGIN WORK;
exec sql UPDATE accounts
SET account-total = account-total * (1+ :interest-rate)
WHERE account-no BETWEEN
:last-account-done+ 1 AND :last-account-done + :stepsize;
exec sql UPDATE batchcontext;
SET last-account-done = last-account-done+ :stepsize;
exec sql COMMIT WORK; /* commit this transaction. */
last-account-done = last-account-done + stepsize;
}
exec sql BEGIN WORK
exec sql DROP TABLE batchcontext;
return;
}
Není to striktně atomické, protože v případě abortu lze jen pokračovat dál. Izolovanost platí
jen pro jednotlivé dávky.
Nyní můžeme říci, co očekáváme od dlouhotrvajících transakcí:
− Minimalizace ztracené práce: Možnost rozdělit rozsáhlou transakci na menší části,
abychom při pádu systému neztratili příliš mnoho udělané práce.
− Obnovitelný výpočet: Dlouhodobé činnosti (den, týden, měsíc, …) musí být rozděleny na
části tak, aby bylo možné provést za běhu systému shutdown (odpovídá pádu systému), tj.
aby bylo možné dočasné zastavení práce bez commitu a bez následného rollbacku. Tady
se mluví o rozšíření ACID vlastností o suspendability, tedy něco jako přerušitelnost.
− Explicitní řízení běhu programu: Systém musí mít pod kontrolou sekvenci transakcí
patřících do jedné dlouhotrvající transakce. V jakékoliv chvíli musí být možnost
pokračovat dál ve výpočtu nebo se vrátit zpět.
29
3.11 Ságy
Nápad vznikl v roce 1987 ([26]) a vychází ze dvou konceptů, se kterými už jsme se setkali:
1. Pracuje se s řetězcem transakcí, tady se ale tomu řetězci říká sága (saga).
2. Z multi-level transakcí se převzal koncept kompenzačních transakcí.
Definice: Sága je množina transakcí s1, s2, …, sn (v jednodušším případě sekvenčně
řazených), kde pro každou transakci si existuje kompenzační (undo) transakce csi.
Výstupem je:
1. Posloupnost s1, s2, …, si, ..., sn v případě, že se provedl commit transakce.
2. Posloupnost s1, s2, …, sj, abort, csj-1,..., cs2, cs1 v případě, že se provedl abort transakce (a
tedy následný rollback s využitím kompenzačních transakcí jednotlivých transakcí ze
ságy).
Obrázek 7: Ságy
3.12 Exotika
Exotické jsou vlastně všechny doposud zmíněné modely kromě flat a distribuovaných
transakcí – jiné se totiž až na výjimky zatím nepoužívají. Existují systémy s transakcemi se
SP, zřetězenými či vnořenými transakcemi, ale obecně neexistují přesná pravidla, jak se s
nimi pracuje.
Předchozí modely vycházely z vlastností ACID a byly popsatelné pravidly. Lišily se
vzájemně jen tím, jakým protokolem se došlo ke commitu nebo abortu. Z trochu jiného
soudku jsou další modely – vycházejí z konkrétních potřeb a rozšiřují ACID pohled na svět.
3.12.1
Kooperující transakce
V některých aplikacích je občas na překážku, že transakce jsou striktně izolované, a že tedy
nemohou mezi sebou nijak spolupracovat. Kooperující transakce (cooperating transactions)
30
je obecný model transakcí, kde je spolupráce mezi transakcemi možná. Jde o trochu jiný
pohled na svět: Motivace vznikla v CAD systémech, kde se práce návrháře s nějakým
objektem chápe jako transakce. Tato transakce je obvykle poměrně dlouhá a je velmi
nevýhodné, pokud návrhář nemá možnost spolupracovat s ostatními návrháři. Hlavním cílem
je umožnit transakci A dočasně "půjčit" objekt jiné transakci B.
Existují některé problémy, které při takovém pohledu na svět vznikají:
− B musí vědět, že jde o půjčený objekt, tj. že si s ním nemůže dělat úplně co chce.
− Na požádání transakce A musí transakce B objekt v rozumném čase vrátit (prerelease
upon request).
− A musí umět říct, že už chce pokračovat v práci s půjčeným objektem (explicit return –
musí si např. pamatovat všechny, kdo si ho půjčili).
− B chce provést commit, ale měl by vrátit objekt.
− B chce provést rollback před okamžik, kdy mu byl objekt půjčen, objekt je ale mezitím
vrácen A.
− …
V kooperujících transakcích se tedy umožní sdílení dat na aplikační úrovni (applicationdriven data sharing). Otázka je, zda jsou ještě transakce, nebo už je to něco úplně jiného. Asi
je nutné specifikovat přesná pravidla konzistence a izolovanosti takových transakcí.
3.12.2
Split transakce
V modelu split transakcí je možné rozdělit jednu transakci ta do dvou transakcí ta a tb. Zavádí
se nová operace split, která transakci rozdělí na dvě části. Operace, které byly provedené do
okamžiku rozdělení transakce, mohou být rozděleny mezi obě následnické transakce, které se
pak starají o jejich commit nebo abort. Operace následnických transakcí mohou být stanoveny
jako nekonfliktní, takže je umožněno sdílení dat mezi ta a tb. Podle toho pak rozlišujeme
sériový (serial) split, kde ta musí provést commit dříve než tb (tb abort-závisí na ta), nebo
nezávislý (independent) split, kde mohou ta a tb provést commit nezávisle na sobě.
Po rozdělení transakce na dvě transakce může být znovu každá z následnických transakcí
rozdělena další aplikací operace split. To vede k vytvoření stromu transakcí, podobně jako je
tomu u vnořených transakcí.
Speciálně může být část operací delegována nějaké transakci vytvořené pomocí split, která
neprovede nikdy žádnou operaci. Taková operace je pak určena pouze k potvrzení
externalizace výsledků pomocí commit, nebo ke zrušení výpočtu pomocí abort. Původní
transakce (splitting transaction) může sdílet data, která má zamknuta. To provede označením
některých operací oddělené transakce (split transaction) jako nekonfliktních (v tabulce
konfliktních operací se udělají výjimky). Oddělená transakce svá data původní transakci sdílet
nemůže. Protože tb vidí výsledky operací ta, musí abort ta vést na abort tb, protože v případě
abortu pouze ta by byl výpočet v rámci tb postaven na neplatných datech.
31
3.12.3
Joint transakce
V joint transakcích je možné ukončit běžící transakci i jiným způsobem než commitem nebo
abortem – je možné nechat transakci splynout s jinou transakcí. Spojující se transakce (ta,
která použije novou operaci joint, se kterou transakcí má splynout – joining transaction)
všechny své zámky, objekty, logy ap. předá nově spojené transakci (joint transaction).
Jakékoliv efekty spojující se transakce na datech jsou externalizovány až v okamžiku
commitu spojené transakce.
Pokud se zkombinují joint transakce se split transakcemi, vzniká už poměrně bohatá možnost
strukturovat výpočty používající transakce.
3.12.4
RCA
Pokud uvažujeme jiné než hierarchické výpočty v souvislosti s dlouhotrvajícími transakcemi,
jeví se jako velmi smysluplný transakční model RCA (recoverable communicating actions). V
tomto modelu může transakce – odesílatel (sender) poslat nějaká data jiné transakci –
příjemci (receiver). Daty se v tomto případě rozumí např. výsledky nějakých operací, nikoliv
objekty, nad kterými transakce pracují. V případě, že odesílatel abortuje, musí abortovat i
příjemce (příjemce je na odesílatelovi abort-závislý). Kdyby tomu tak nebylo, pak by
příjemce pracoval s neplatnými daty, která by pocházela z abortované transakce.
Skupiny transakcí, které jsou vzájemně propojené abort závislostmi, musí commitovat
současně, protože kdyby některá z transakcí commitovala dříve, nebylo by možné commit
vrátit v případě, že jiná transakce abortovala. Takové skupiny transakcí vlastně tvoří jakousi
transakci vyššího řádu. RCA umožňují částečný rollback těchto transakcí vyššího řádu,
protože transakce, která je abort závislá na jiné transakci, může abortovat aniž by způsobila
abort ostatních transakcí ze skupiny (abortovány jsou jen na ní abort-závislé transakce).
3.12.5
ConTract model
3.12.6
Unifikovaný transakční model
Z předchozích kapitol vidíme, že transakčních modelů je poměrně hodně, každý z nich se dívá
na problém bezpečných, izolovaných a atomických operací trochu jinak. Místo toho, abychom
se soustředili na vymýšlení nějakého nového transakčního modelu, který je ad-hoc řešením
nějakého problému, zkusíme hledat společné rysy všech transakčních modelů.
Cílem by mělo být co nejlépe navrhnout taková primitiva, která by umožňovala pracovat s
transakcemi ve smyslu unifikovaného transakčního modelu, který nepodporuje omezenou
množinu nám známých transakčních modelů, ale dovoluje pracovat s uživatelsky
definovaným (tedy vlastně v jistém smyslu libovolným) transakčním modelem.
V systému ASSET [11] jsou transakční primitiva rozdělena na základní (basic) a rozšířená
(advanced). K základním primitivům patří nám známé operace jako begin, commit, abort,
32
ale kromě toho třeba setTransactionTimeout, getParent ap. Mezi rozšířená primitiva
patří:
− Delegování: delegate( )
− Povolení sdílení: permit( )
− Vytvoření závislosti: create_dependency( )
3.13 Historické poznámky
− 1970: Larry Bjork, Charles Davies: Spheres of Control
− IMS: Jeden z prvních databázových systémů s něčím, co se podobalo transakcím.
Zaměření na atomicitu, concurrency control. Poprvé i náznaky zřetězených transakcí.
− 1976: K. D. Eswaran, J.Gray vycházeli z IMS a napsali články dnes považované za
klasiku v oblasti flat transakcí.
− 1979: System R – použití savepointů.
− 1981: J. Gray publikuje článek, ve kterém identifikuje nedostatky flat transakcí. Poprvé
začíná v souvislosti s transakcemi uvažovat o modelování struktury a běhu aplikací.
− 1981: Elliot Moss: nested transakce během projektu Argus. Využití tohoto modelu pro
DML (data manipulation language)
− 1987: H. Garcia-Molina, K. Salem: sagas (jinak o dlouhotrvajících transakcích se
uvažovalo s malým úspěchem odjakživa).
− 1987: A Reuter, S Klein: migrating transacitons (řešení dlouhotrvajících transakcí).
− 1989: Y. Leu: flexible transactios (řešení dlouhotrvajících transakcí).
− 1989: A. Reuter: ConTracts (řešení dlouhotrvajících transakcí).
− 1991: ACTA
− 1991: Camelot s persistentním programovacím jazykem Avalon
− 1991: Encina TP Monitor s Transactional C
V poslední době: Business transactions, shopping card transactions, on-line transactions,
Internet transactions…
Jiný pohled: rozšíření transakcí tak, aby se zohlednila práce nad sdílenými daty. Modely, o
kterých jsme se v předchozích kapitolách zmiňovali, dodržují izolovanost transakce (navenek
nesmí být před commitem vůbec poznat, že transakce běží).
Poslední věci:
1. Popis transakce jako konečného automatu pro řízení commit protokolu.
2. Popis závislostí při splnění izolovanosti na sdílených datech (nová sémantika, grafy
podobné obrázkům, které jsme si kreslili u jednotlivých modelů).
J. Klein, 1991: teorie závislostí transakcí založená na konečných automatech s popisu chování
pomocí pravidel temporální logiky.
33
4. TP systémy
TP systém je systém zahrnující operační systém, sítě, jednu nebo více databází, generátory
aplikací, ap. Současně obsahuje alespoň jeden TP monitor, který řídí práci s transakcemi.
Transakcemi rozumíme ACID operace. Základními službami (core services) TP-systému
(tedy vlastně potažmo transakčního monitoru) jsou:
−
−
−
−
TRPC,
Transakční Manager,
Log Manager,
Lock Manager.
4.1 Pohled koncového uživatele na TP-systém
Příklad: elektronický poštovní (e-mail) systém. Uživatel má klienta, kde může provádět např.
následující atomické operace:
−
−
−
−
−
−
−
Send,
Create,
Reply,
Delivery (musí se ověřit všichni adresáti, replikace),
Delete,
Read,
Cancel (kompenzační transakce k send).
Koncovým uživatelem je v tomto případě člověk. Všechno se na něj tváří jako jednoduchá
operace, ve skutečnosti je to složitější, díky vlastnostem ACID to funguje. Tj. transakce jsou
pro koncového uživatele zcela transparentní.
Koncovým uživatelem nemusí být nutně člověk: často jsou to různá zařízení. Příklad: Pec
provádí atomickou operaci vývoz sochoru. Ve skutečnosti jde o řadu dílčích operací, které
musí všechny proběhnout (otevření pece, rozjetí dopravníku v peci, rozjetí dopravníku za
pecí). Když cokoliv selže, ostatní operace se neprovedou. Při rollbacku se použije
kompenzační transakce, např. se zapne zpětný chod dopravníku ap.
4.2 Pohled správce TP-systému
Administrátor e-mail systému musí:
− Umožnit práci jednotlivým uživatelům (hesla, zdroje, školení). Používá k tomu systém
umožňující pomocí ACID operací měnit hesla, ap.
− Koordinovat práci různých softwarových systémů, atomicky provádět update.
− Řídit administrativu spojenou s transakcemi. Používá repository. Pokud má velké
množství terminálů na kterých dochází k chybě jednou za tři roky, může to znamenat, že u
něj (v administrativním centru) řeší chybu každých 10 minut.
34
− Řídit výkonnost systému. Existují dávkové (batch) transakce, které jsou větší co do
objemu práce, ale jejich provedení může být eventuálně pozdrženo, a interaktivní
(interactive) transakce, které jsou menší, ale je jich větší množství a musí být provedeny
ihned. Celý systém se musí dynamicky ladit, v distribuovaném prostředí se přidávají nové
prostředky ap.
4.2.1 TPC
Jednou z důležitých vlastností transakčního systému je samozřejmě jeho výkon. Transaction
Processing Performance Council (TPC) je sdružení firem vytvářející univerzální benchmarky
(pro různé typy a velikosti počítačů, sítí, databází). Jednotkou jsou transaction-per-second
(tps). Poměr cena/výkon se pak počítá tak, že se cena hardware, software a údržby za pět let
provozu systému vydělí příslušným údajem tps. Další informace:
− Členy jsou výrobci počítačů a databází i koncoví uživatelé (40 členů - IBM, Informix,
Dell, HP, Sybase, Sun, Intel, Microsoft, Oracle, Hitachi, Netscape, Bull, ...).
− Publikace nových výsledků měření, čtvrtletní TPC Report se všemi TPC testy.
− Nabídka užitečných nástrojů pro měření.
− Tvorba benchmarkovacích programů.
− Pravidelná TPC setkání.
− Roční poplatek je $9500.
− Domácí stránka je www.tpc.com.
Benchmarky jsou následující:
− TPC-A: Jednoduchá bankovní transakce se 100 bytovým vstupem, 4 databázovými
updaty a 200 bytovým výstupem na terminál (jednoduchá prezentační vrstva –
presentation service). Databázové operace kombinují přístup do hlavní paměti se
sekvenčním a náhodným přístupem. Do ceny se počítá 10 terminálů na tps.
− TPC-B: To je TPC-A s dalšími terminály, sítí a dalšími omezeními na trvalou paměť.
Používá se na testy databází a dává asi desetkrát vyšší údaj než TPC-A.
− TPC-C: Používá běžnou aplikaci, ve které jsou smíchané dávkové a interaktivní
transakce. Obsahuje fronty transakcí, aborty transakcí, jednoduchá prezentační vrstva.
Hodnota bývá asi desetkrát menší než TPC-A. Je považován za standard.
− TPC-A, TPC-B, a TPC-C jsou nejznámější. Specifikace zahrnuje:
− logický návrh databáze, tabulky, relace, integritní pravidla,
− transakce typicky nabízené systémem (dotazy, vstupy, výstupy – obrazovky na
jednoduché znakové terminály),
− vlastnosti transakcí (isolační levely, konzistenční kritéria, bezpečnost, durability),
− naplnění databáze, pravidla pro míru přidávání/odebírání dat,
− specifikace metriky pro rychlost,
− architektura systému, front end, back end, komunikace, počty uživatelů, terminálů,
podmínky konkurentního přístupu více uživatelů/transakcí.
− TPC-D: Pro decision support benchmarkování a OLTP (on-line TP, Internet).
− TPC-S: Pro servery (bez front end).
35
− TPC-E: Enterprise benchmark, robustnější než TPC-C (který je robustnější než TPC-A),
pro zvlášť robustní, rozsáhlé systémy.
− TPC-C/S: Benchmark pro client/server systémy.
− TPC-W: WWW commerce benchmark (nejnovější).
Používání TPC testů je poměrně komplikované. Vytvořit TPC test je velmi pracné, ale dává
velmi dobré výsledky, porovnatelné s jinými výsledky, které jsou v časopisech a jiných
publikacích.
Přesné specifikace benchmarků jsou v [27] nebo na www.tpc.com.
4.3 Pohled návrháře na TP-systém
Existuje klientská a serverovská část aplikace. Na straně serveru vystupuje TP monitor, který
řídí přihlášení, kontroluje oprávnění k provádění operací na serveru, drží případný kontext
operace, ap. Návrhář musí napsat obě části aplikace – klientskou a serverovskou.
Transactional RPC (TRPC) je spojení myšlenky RPC a transakčního zpracování. BEGIN
WORK začne transakci a na serveru vytvoří kontext transakce (trid). Všechny následující
operace se provádějí nad určitým kontextem. Kontext se zruší po zavolání COMMIT WORK.
Resource managers – všechno, co umožňuje přístup ke vzdáleným resource (data, procesy).
TP monitor jako součást resource manageru řídí commit protokol – nabízí funkce rollback,
commit, ap.
4.4 Pohled resource managera na TP-systém
Přístup na nějaký server může být vzdálený (remote) nebo lokální (local). Většinou mezi nimi
pro transakčního managera není žádný rozdíl.
Existuje velké množství sítí, databází, programovacích jazyků, presentačních aplikací. Každý
z resource managerů funguje s nějakou podmnožinou z těchto aplikací.
Základní služby (core services):
−
−
−
−
TRPC,
Transakční Manager,
Log Manager,
Lock Manager.
4.5 Transakčně-orientovaný výpočetní styl
Používání transakcí indukuje jakýsi nový transakčně-orientovaný výpočetní styl (transactionoriented computing style). Srovnání s ostatními výpočetními paradigmaty:
36
− Batch processing: Velké kusy výpočtů s velkou alokací resourců, sekvenční přístupové
metody, aplikace se zotavuje sama, relativně málo (desítky) konkurentních výpočtů,
izolovaná exekuce.
− Multiprogramming: Pipelining programů a resourců.
− Time-sharing: Jeden proces na terminal (session), velká alokace resourců,
nepředvídatelné požadavky, sekvenční přístupové metody, aplikace provádí zotavení,
stovky konkurentních uživatelů.
− Real-Time processing: Událostmi řízené operace, opakované spouštění procesů,
dynamické přidělování zařízení úlohám, izolovaná exekuce, vysoká dostupnost
(availability), velký výkon.
− Client-server processing: Nová verze time-sharing.
− Transaction-oriented processing: Sdílení, různé požadavky opakovaná exekuce, často
jednoduché funkce, některé z nich dávkové, mnoho terminálů (OLTP), inteligentní klienti,
vysoká dostupnost, systém provádí zotavení automaticky, automatické zajištění stability
dat, automatické vyvažování zátěže.
4.6 Taxonomie spouštění transakcí
1. Přímé (direct) versus frontované (queued) transakce: Celkem zřejmý rozdíl, jen je nutné
podotknout, že frontované transakce jsou takové, kde se ví o tom, že existuje nějaká
fronta. Pokud je použití fronty odstíněno TP monitorem tak, že to vypadá jako přímý
přístup, není to frontované transakce.
2. Jednoduché versus komplexní transakce: Jednoduché transakce jsou takové, kde je
jednoduchá vstupní zpráva, která se poměrně rychle zpracuje a provede se commit. Pokud
chce klient dále komunikovat se serverem, zahájí novou transakci. Nepoužívá se příliš
mnoho prostředků (desítky). Naproti tomu komplexní transakce trvají delší dobu a
dochází k opakované výměně zpráv mezi klientem a serverem. Používá se větší množství
prostředků (desítky tisíc).
3. Lokální versus distribuované transakce: zřejmý rozdíl, zmínka o distribuovaných
transakcích je rovněž v kapitole 3.7.
Obrázek 8: Taxonomie spouštění transakcí
37
4.7 Služby transakčního systému
Databázový systém je příklad systému, který nabízí transakční služby, služeb ale musí být
obecně přece jen více. Pokusme se je vyjmenovat:
− Řízení heterogenity: To, že jednotlivé části systému (resource managers) umí zachovávat
u sebe lokálně ACID vlastnosti neznamená, že jsou ACID splněny celkově. Je potřeba
jednotlivé transakce řídit jako součást jediné transakce.
− Řízení komunikace: Je-li součástí transakce nějaká komunikace, musí existovat vhodná
podpora pro to, aby byl i její stav zahrnut do transakce (TRPC).
− Řízení terminálů: Komunikace s různými druhy terminálů, tj. zprávy od terminálů,
snímačů magnetických karet, bankomatů, ap. musí být zahrnuty do transakce, stejně tak
návratové zprávy.
− Presentační služby: Různé terminály používají sofistikované presentační služby (X
Windows). Např. restart prostředí do takové stavu, v jakém bylo před pádem systému,
musí být taky ACID – jako ostatní resource managery.
− Řízení kontextu: Udržování kontextu transakce pro jednotlivé uživatele, terminály,
aplikace.
− Start/restart: Po restartu je nutné uvést celý systém do konzistentního stavu.
Toto byly jen služby, které mají vliv na to, jak se programují aplikace pro transakční
prostředí. Jiné jsou: řízení konfigurací (configuration management), autorizace
(authorization), vyvažování zátěže (load balancing), administrace, ap.
4.8 Procesy TP systému a jejich struktura
Obrázek 9: E-R model znázorňující procesy transakčního systému
Na obrázku vidíme E-R model zahrnující (všechny) entity při zpracování nějakého
požadavku. Z obrázku pak vyplývá, jaké jsou nedostatky jednotlivých architektur TPmonitoru:
38
1. Jeden proces na terminál: Každý "terminál" získá jeden proces na serveru, který se o něj
stará. Zjevné nevýhody:
− Příliš mnoho procesů na serveru (co když existuje 10000 terminálů, co když většina z nich
vlastně nic nedělá).
− To souvisí s příliš mnoho přepnutími kontextu (process switches), které jsou dost náročné
(2000 – 5000 instrukcí).
− Ne každý typ terminálu potřebuje všechny vlastnosti, které nabízí použití procesu
(bankomat nebude chtít používat kompilátor).
− Příliš mnoho řídících struktur (control blocks) – řídících struktur o otevřených souborech
databázích navázaných spojeních ap. Problém polynomiální exploze (polynomial
explosion) – násobí se počet terminálů krát počet databází krát počet kurzorů ap.
− Málo flexibilní vyvažování zátěže (load balancing) – priority řídí operační systém, nikoliv
TP monitor.
Použitelné je to jen v případě menších systémů (desítky až stovky klientů).
2. Jediný terminálový proces: TP monitor by mohl dávat priority jednotlivým
požadavkům, měl by všechno "pevně v rukou". Nevýhody:
− Takový proces musí znát všechny možné protokoly.
− Omezení jedním adresovým prostorem (např. nevyužívá se multiprocesorů) – velké
množství řídících struktur.
− Každá vážná chyba (např. page fault) vede ke spadnutí všech spojení (sessions).
− Jeden proces je úzké hrdlo (bottleneck – opět problém např. s multiprocesory).
3. Mnoho serverů, jeden plánovač: Oddělení presentační vrstvy od vlastního zpracování
vstupních požadavků. Existuje jediný proces, který řídí příjem a distribuci požadavků a
několik aplikačních procesů. Aplikace může mít více procesů (server class), jednotlivé
aplikace komunikují jen s příslušnými zdroji, jsou navíc chráněny vůči sobě navzájem,
protože jsou v oddělených adresových prostorech. Proces, který přijímá požadavky od
klientů, vyřizuje autentikaci klientů, identifikaci jednotlivých požadavků a jejich distribuci
jednotlivým aplikačním procesům. Problém: Presentační proces je v případě velkého
množství požadavků úzkým hrdlem komunikace.
4. Mnoho serverů, mnoho plánovačů: Zobecňuje to předchozí architekturu: existuje více
funkčně shodných procesů, které přijímají a dále distribuují požadavky klientů.
5. Transakční monitory
Připomeňme si úvodem, co je transakční monitor. Transakční monitor (nebo také TP monitor)
se stará se o řízení a monitoring transakcí. Jeho jádrem je transakční manager (který se stará o
vlastní řízení transakcí, o řízení jejich stavů, řízení commitů a restartů ap.), dále využívá
služby log managera a lock managera.
39
Obrázek 10: Služby transakčního monitoru
Na obrázku jsou služby, které využívají aplikační programy. Zvýrazněné služby bývají
typicky nabízeny transakčním monitorem.
5.1 Komponenty transakčního monitoru
V této kapitole si povíme blíže o službách, které nabízí transakční monitor. Některé
z následujících služeb mohou být implementovány komponentami, které nutně nemusejí být
součástí balíku s nálepkou "transakční monitor":
− Prezentační služby: Definují interface pro odstínění programátora od některých
technických detailů jako jsou formáty zpráv, typy terminálů. Veškerá komunikace
s transakčním monitorem prochází vrstvou prezentačních služeb.
− Queue management: Zajištění příjmu požadavků, jejich zařazení do správné fronty
požadavků, doručení výsledků zpátky klientovi. Mohou být zvoleny doručovací strategie
at least once, at most once nebo exactly once. Stav fronty záleží na stavu příslušných
transakcí - požadavky jsou ve frontě pouze pokud klient, který požadavky generoval,
commituje.
− Server class management: Vytvoření instance procesu, který zpracovává požadavky,
vytvoření front, natažení kódu do správných adresových prostorů, nastavení práv
procesům, přepínání kontextu, nastavení priorit.
40
− Plánování a časování požadavků: Požadavek se posoudí, zda může být zpracován
lokálně, pokud ne, tak se pomoci TRPC pošle jinému transakčnímu monitoru. Pokud ano,
tak se posoudí, zda se má vytvořit nový proces pro zpracování požadavků, zapojí se
algoritmy pro vyvažování zátěže.
− Autorizace požadavků: Posouzení oprávněnosti požadavku, statická autorizace nebo
value-dependent dynamická autorizace kde záleží na typu a obsahu požadavku.
− Řízení kontextu: Jedna část je o uložení všech dat, které se týkají transakce, aby mohly
být použity v kontextu dané transakce (obvykle se používá nějaká SQL databáze). Druhá
část se týká toho, že transakce často využívá více serverů a resource managerů a
mezivýsledky operací by se měly posílat z jednoho serveru na druhý. Lepší je neposílat
všechna data související s transakcí najednou, raději je nechat na TP monitoru a nabídnout
služby pro jejich sdílení.
Se všemi službami souvisí nutnost rozsáhlé repository obsahující např. seznamy všech uzlů,
softwarových nebo hardwarových komponent (třeba terminálů, log managerů) instalovaných
programů, přístupových práv, uživatelů a jejich rolí a profilů, konfigurace serverů, atd.
Podíváme-li se na TP monitor jako na softwarovou komponentu, měla by tedy mít následující
seznam interface:
− Interface pro nastartování a restart: V okamžiku restartu musí být nastartovány
všechny resource managery.
− Interface pro definici nového resource managera: Nový resource manager musí být
explicitně přidán do repository.
− Interface pro změnu konfigurace server-procesu.
− Interface pro zpracování TRPC požadavků: To je vlastně nejdůležitější interface, přes
který se přijímají požadavky ohraničené begin a commit.
5.2 Transakční RPC
Je potřeba si uvědomit, že TRPC je mnohem komplikovanější než normální RPC. Běžné
TRPC volání používá další resource managery, kteří používají další TRPC, ap. Všechny
resource managery, které jsou volány pomocí TRPC se stávají součástí transakce. TP monitor
spravuje množství procesů a každé TRPC volání musí být označeno jednoznačným
identifikátorem. Pokud se volání distribuuje dál do jiných TRPC, musí být tento identifikátor
udržován i v těchto TRPC. Pokud vznikne někde nějaký proces nebo se pošle zpráva, musí se
vědět o tom, že je součástí tohoto TRPC.
Kdyby šlo jen o to, že se musí ve všech resource managerech vědět, o příslušném
identifikátoru TRPC, tak je to jednoduché. Ale všechny akce na všech zúčastněných resource
managerech musí proběhnout jako součást jediné transakce. Aby taková síť účastníků
transakce spolupracovala, je nutné:
− Řízení účastníků: Je nutné udržovat seznam resource managerů, kteří spolupracují na
transakci. Před konečným commitem musí commit potvrdit každý resource manager (two
phase commit protocol) – to zařizuje transaction manager. Jsou resource managery, které
žádný commit nepotvrzují (jednodušší servery) – s tím je nutno počítat.
41
− Chránění informací o transakcích: Některý resource manager může být volán během
transakce i několikrát. Každé volání může zpracovat jiný proces a každé volání může být
iniciováno jiným procesem.
− Podpora transakčních protokolů: Zajištění ACID vlastností na všech resource
managerech pomocí TRPC.
Jedna věc je asociace požadavků, zpráv procesů ap. s nějakým identifikátorem transakce.
Druhá věc je koordinace resource managerů.
5.3 Interface resource managera
Resource manager má několik rozhraní, klasické rozhraní pro podporu transakcí vypadá asi
takto:
Boolean rm_Prepare()
Boolean rm_Rollback_Savepoint(SP);
Boolean rm_Commit(Boolean);
void rm_UNDO(&buffer);
void rm_Abort();
void rm_REDO(&buffer);
void rm_Checkpoint();
void rm_Restart(LSN);
boolean identify(RMID)
Některé resource managery jsou méně sofistikované a nemají podobné rozhraní, které
podporuje transakční protokol. Při potvrzování commitu se pak např. implicitně předpokládá
odpověď ANO (je to třeba v případě, kdy resource manager nepracuje s trvalými daty).
Volání funkce identify oznamuje transakčnímu manageru, že resource manager je
připraven přijímat požadavky, specielně po restartu, sekvenci operací undo, ap.
Další rozhraní jsou rozhraní pro používání jednotlivých služeb jinými resource managery a
rozhraní pro používání služeb jiných resource managerů.
TRPC nejsou volána z klientů přímo na resource managery – prostředníkem jsou TP
monitory. Proto musí resource managery nabízet další rozhraní, pomocí kterých se je TP
monitory administrují:
RMID rmInstall (RMNAME, &rm_callbacks, ACL, stuff);
boolean rmRemove(RMID);
boolean rmDeactivate(RMID);
boolean rmActivate(RMID);
void rmStartup();
void rmShutdown();
Funkce rmInstall vrací seznam všech callback entry pointů, které daný resource manager
podporuje. Pokud není některá funkce podporována, jsou potřeba další informace. Lze říct, že
42
když není implementováno potvrzení commitu, že to znamená implicitní ANO. Nelze ale
naproti tomu nic usoudit o resource manageru, který neimplementuje funkci savepoint.
Resource managery musí být udělány tak, že s nimi nemůže pracovat nikdo přímo, vždy musí
být použito TP monitoru jako prostředníka. TP monitor koordinuje provádění transakcí na
jednotlivých resource managerech v rámci jedné transakce, kromě toho provádí autentikaci,
vyvažování zátěže, ap.
5.3.1 Resource Manager Calls versus Resource Manager Sessions
Problém s udržováním kontextu: Na nějakém resource manageru je provedeno několik
operací, které patří ke stejné transakci. Kontext transakce by měl být na resource manageru
nějak udržován. Situaci ztěžuje to, že jednotlivé operace mohou být provedeny různými
procesy a dokonce mohou být vyvolány různými klienty. Existují následující scénáře
komunikace:
1. Nezávislá volání: Jednotlivé operace na serveru jsou volány nezávisle na sobě.
2. Sekvence volání: Operace typu „chci následujících deset záznamů z databáze“. Je nutné
znát výsledek předchozí operace volané se stejným kontextem.
3. Komplexní interakce: Výsledky jednotlivých operací musí být na serveru uchovány do
celkového commitu. Příklad: poslání e-mailu je totéž, co vytvoření hlavičky, vytvoření
těla, ap. Celkový commit závisí na všech operacích, ty ale mohou být vyvolány klidně v
libovolném pořadí.
Tyto scénáře navozují debatu, zda má být resource manager session oriented nebo servicecall-oriented. Session znamená, že se klient a server domluvili, že spolu budou nějakou dobu
komunikovat a oba si uchovávají o tomto stavu nějaké informace. Tento problém je řešen na
více úrovních, transakční zpracování přidává tu vlastnost, že na serveru musí být stejně uložen
nějaký kontext, aby se mohly dodržovat ACID vlastnosti. Možnosti udržování kontextu jsou
následující:
1. Kontext udržuje komunikační systém – je vytvořena session. Server je tedy jakoby
"namapován" ke klientovi.
2. Posílání kontextu při volání každé operace a zpět v návratové zprávě. Problém je v tom, že
klient nemusí vědět, že je kontext potřeba (případ 3 z předchozího – žádost o povolení
commitu nepočítá s tím, že je potřeba status všech předchozích operací).
3. Uložení kontextu mimo server – v databázi. Možné použít pro více serverů.
4. Uložení kontextu ve sdílené paměti – podobné jako s databází, jen oba servery musí běžet
na stejném uzlu. V obou případech musí být zajištěna synchronizace přístupů.
Z pohledu serveru ještě rozlišujeme:
− Client-oriented context: Kontext přísluší nějakému klientovi.
− Transaction-oriented context: Kontext patřící jedné transakci může příslušet operacím
vyvolaným různými klienty.
Běžné komunikační systémy počítají jen s kontextem prvního druhu. Každý resource manager
musí být schopen držet kontext druhého druhu, pokud pracuje s persistentními daty.
43
5.4 Funkční princip TP monitoru
Nemá cenu zde popisovat podrobně jednotlivá rozhraní. Strukturu TP monitoru nejlépe
charakterizuje následující obrázek.
??? slide str. 319 ???
5.5 Transakční fronty zpráv
Důvody, proč nepoužívat přímé (direct) transakce, ale frontované (queued) transakce:
− Load control: Pokud nastane chvilkové přetížení, sníží se výkon systému. Nevyplatí se
situaci řešit přidáním nového procesu (zabere to nějaký čas a navíc už se třeba ten proces
nikdy potom nepoužije).
− End-User control: Výsledky operací by na uživatele měly čekat, dokud nepotvrdí jejich
příjem. Je nesmysl, aby kvůli tomu čekal celý proces – proběhne celý protokol a výsledky
si na uživatele počkají ve frontě.
− Recoverable data entry: Některé systémy nejsou konfigurovány na nejmenší dobu
odezvy, ale na větší průchodnost. Data se neztrácejí, nezahlcují server, čekají ve frontě a
jednou na ně dojde řada.
− Multi-transaction request: Pokud by transakce byla zpracovávána řetězcem serverů,
dojde k paralelnímu zpracování, aniž bychom se příliš museli starat o rozvrh práce pro
jednotlivé servery. Mezi servery by totiž byly fronty, které vyrovnávají zatížení na
jednotlivých serverech. To je trošku speciální případ, ale v praxi nastávají podobné
situace, kde použití front na všech úrovních zvýší průchodnost sytému.
Jsou dva typy front: volatile a durable.
5.5.1 Volatile fronty
Slouží k implementaci direct transakcí (jsou "neviditelné"). Pokud je na vstupu serveru více
požadavků než je obsluhujících procesů, mohou se požadavky, na které nezbyl proces,
zamítnout, nebo vytvořit další proces, ale nejlépe je dát je do fronty, dokud nebude nějaký
proces volný. Skutečná implementace je samozřejmě trochu složitější, zvlášť s přihlédnutém
k faktu, že do fronty vkládají požadavky různí uživatelé, různé aplikace ap. Organizace fronty
je obvykle FIFO, mohou být použity jiné metody plánování. Přístup ke frontě musí být
vhodně chráněn.
5.5.2 Durable fronty
Slouží pro asynchronní transakční zpracování. Je nutné si uvědomit, že asynchronní
zpracování je zásadně odlišné od OLTP. Když klient předá požadavek do fronty, může
provést commit své transakce a zahájit další transakci. Pak čeká, až se ve frontě odpovědí na
požadavky něco objeví. Server čeká, až se objeví ve frontě požadavků nějaký požadavek, pak
zahájí transakci. Po provedení požadované operace dá výsledky do fronty odpovědí a provede
commit transakce. Neboli: Pro každou žádost existují tři transakce (klientská žádost,
serverovský výpočet a klientské zpracování výsledku).
44
Resource manager může mít více front – buď se stejnou funkcionalitou, nebo pro různé druhy
požadavků.
Problémy s frontami: Jedna transakce vezme první prvek z fronty, druhá vezme druhý. V
případě abortu první transakce musí druhá transakce pracovat s prvním prvkem z fronty!
Čekat na dokončení předchozí transakce není příliš dobrý nápad – značně by se zpomalilo
zpracování. Řešení: Transakce to zkoušejí, občas zjistí, že pracují na nesprávném požadavku
v nesprávnou dobu a provedou abort.
Jiný problém: klasické chování při abortu by mělo být takové, že požadavek po rollbacku
zůstane ve frontě, jako by žádná transakce nikdy nezačala. Pak by ale požadavek z fronty byl
znovu zpracováván nějakými servery (i opakovaně tím samým) donekonečna. Takového
požadavku je potřeba se nějak zbavit. Třeba snižovat při každém neúspěšném pokusu o
splnění požadavku nějaký čítač; v případě vynulování čítače požadavek definitivně z fronty
zrušit. To je ale podle definice transakce nepřípustné: po abortu je na frontě durable update.
Možná řešení: Transakce, která by měla provést abort, se prohlásí za úspěšnou (použití
prvního savepointu). Nebo se zkrátka update fronty prohlásí za unprotected action.
Fronty mohou být ASAP, timed (zpracování požadavků z fronty až po nějakém čase),
threshold (fronta je zpracovávána až pokud je v ní určitý počet požadavků – pro případ, že je
náročné zahájit práci s nějakým zařízením, ap.).
5.6 Další úlohy TP monitoru
Aneb na co jsme zapomněli - vyvažování zátěže, autentikace, autorizace, zotavení z chyby.
5.6.1 Vyvažování zátěže
Zřejmě všichni vědí o co jde, totiž o rozvržení zátěže za účelem optimalizace čehosi,
nejčastěji doby na provedení operace. Stojí za to zmínit fakt, že konfigurace systému se dá
rozdělit na dvě části - statickou a dynamickou, a vyvažování zátěže se týká jen té dynamické.
Krom toho statičnost může být buď inherentní (oblíbený příklad s raketami, tam se zátěž
rozkládá špatně), nebo prostě jen daná konfigurací (administrátor spustí nameserver u sebe
doma, protože tam se mu dobře instaloval).
Takže je vidět, že vyvažování zátěže nejde aplikovat na všechno. Otázkou je, kde dává smysl
rozkládat zátěž. Proč tato otázka není triviální (káru přece rovnoměrně naloží každý)? Jedním
problémem je interdependence of local loads, dalším data affinity, dalším context transfer a
dalším decision overhead.
− Interdependence of local loads: Typickým příkladem je rozdělení zátěže mezi thready na
single-CPU systému. Tam se sice udělá spousta threadů, ale všechny užírají z jednoho
koláče, takže jediným přínosem je scheduling overhead. Odbočka: proč se tedy vlastně
dělají multithreaded servery, fakt že počítač hlavně čeká, relevance s load value.
Interdependence nemusí být způsobena jen takhle okatě, stačí když se vezmou konflikty
na sdílených datech. Důsledkem je potřeba, aby TP monitor sledoval zátěž všeho
možného, a na základě těchto informací vynášel informovaná rozhodnutí (třeba zatížení
linky a cílového uzlu vs. lokální zatížení) – což je typická black magic.
45
− Data affinity: Výpočet chce k sobě data, pokud ho nešikovně přenesu, zvýším traffic a
výsledkem může být zhoršení výkonu. Problém je jak predikovat co bude vlastně výpočet
dělat ještě než ho přesunu. Takže výsledkem je spíš algoritmus, který podobné problémy
detekuje a informuje o nich někoho, kdo má na starosti statickou konfiguraci.
− Context transfer: Pokud rozložím výpočet, musím udržovat nějaký inter-node context,
ten bude růst s počtem uzlů, na které je výpočet rozložený.
− Decision overhead: Zmíněné problémy naznačují, že k informovanému load balancing
rozhodnutí je potřeba spousty informací, které získávat také něco stojí - jde o to, aby to
nebylo víc než kolik vydělám.
5.6.2 Autentikace, autorizace
Existuje nějaký systémový administrátor (k těmto věcem autorizovaný, tedy s vysokou
autoritou), který autorizuje určitého uživatele k určitým činnostem v systému (třeba
operačním). Uživatel potom může sednout k terminálu a přihlásit se pomocí svého uid a hesla.
Systém při přihlášení provádí autentikaci. Autentikace je ověřování identity, zatímco
autorizace je zjišťování oprávnění dělat určitou činnost.
Problémy jsou například tyto:
− Kdy autentikovat, kdy autorizovat: Zřejmě jak a co. Uživatele při sednutí za terminál je
potřeba autentikovat, všechny jím spuštěné procesy mívají jeho uid (výjimka – v UNIXu
efektivní uid při spuštění některých programů) a authid. Pro požadavky se vyžaduje
příslušná autorita, někdy se použije bind-time autorizace.
− Na jaké granularitě: Také jak a co (tohle zní triviálně až do té doby, než člověk zjistí, že
mu chybí nějaká úroveň granularity - viz konfigurace CISCA).
− Jak delegovat autoritu - to je dost obecný problém, totiž server má vždy autoritu klienta,
na jehož žádost požadavek vykonává, plus nějakou svojí autoritu (kterou klient nesmí
zneužívat). Tohle začne být komplikované ve chvíli, kdy server volá další server - má mít
autorizaci prvotního klienta nebo svojí nebo snad obě? Zpravidla se pronáší prvotní klient.
5.6.3 Zotavení z chyb
A samozřejmě - TP monitor je to zvíře, které při startu systému kouká, kde to vlastně všechno
naposledy spadlo. Mechanismus je jednoduchý - nastartovat všechny resource managery,
podívat na čem všem se dá udělat commit, to commitnout, na zbytku provést abort. Používají
se UNDO a REDO logy, o těch se ale budeme podrobněji bavit později.
6. Concurrency Control
Každá akce programu je popsaná invarianty nad stavem (index >= 0 and index < items) a
invarianty nad změnami. Tyto invarianty nelze vždy splnit (hlavně invarianty nad změnami,
které popisují současný update více věcí), takže systém v podstatě není konzistentní, ale
pohybuje se mezi konzistentními stavy. No a když se spustí současně několik akcí, jde o to,
aby to všechny rozchodily. Tedy chceme:
− Současné vykonávání několika akcí by nemělo způsobovat žádná selhání.
46
− Současné vykonávání několika akcí by nemělo být o mnoho pomalejší než jejich postupné
vykonávání.
Nejprve se vrhneme na nejobvyklejší řešení, totiž zamykání. Jeho princip je triviální,
jednotlivá řešení se liší zejména v granularitě zamykání (podrobnější granularita zlepšuje
concurrency, ale zvyšuje overhead).
6.1 Závislosti
Představme si transakci jako operaci, která podle vstupů IN změní výstupy OUT. Je zřejmé,
že transakce by neměla stavět na datech, která jiná transakce mění – z toho vyjde
FOREACH(i,j): OUT(i) ∩ (IN(j) ∪ OUT(j)) = 0.
Na tom lze postavit mechanismus statického zamykání – na začátku každé transakce se
zkontroluje, jestli její vstupy nekolidují s výstupy běžících transakcí, a pokud ano, počká se.
Samozřejmě je problém, jak na začátku transakce zjistit, co za data bude transakce chtít.
Pesimistický odhad vede k malé granularitě a snížení stupně paralelismu.
O něco lepší je dynamické zamykání – transakce si za běhu zamyká a odemyká co potřebuje a
nepotřebuje. Tady už není určení dvou závislých transakcí tak snadné, protože je potřeba
představit si transakci ne jako jednu, ale jako posloupnost operací.
Řekněme, že každá transakce je posloupnost operací READ a WRITE. Závislosti mezi
transakcemi zjevně zavádějí posloupnosti READ1(X)-WRITE2(X), WRITE1(X)-READ2(X),
WRITE1(X)-WRITE2(X) (vždy se tím stanoví, že T1 je před T2, i když případ WRITEWRITE je mírně řečeno speciálním případem). Z těchto závislostí se dá udělat graf, na kterém
je intuitivně vidět, že v závislostech vadí cykly.
Občas je užitečné pojmenovat některé "špatné" případy:
− Lost update: Udělám zápis, ale někdo ho přepíše svým zápisem postaveným na datech,
která jsem já svým zápisem zneplatnil.
− Dirty read: Podaří se mi přečíst pracovní verzi nějakých dat.
− Unrepeatable read: Přečtu nějakou verzi, která je sice platná, ale zatímco s ní budu
počítat, někdo mi jí přepíše.
Zajímavé je, že tohle jsou všechny "špatné" případy - nic jiného se už stát nemůže. Tedy stačí
vyřešit tyto tři a problém souběžného přístupu k datům je úspěšně vyřešen.
6.2 Formálněji
Mějme akce READ, WRITE, RLOCK, RWLOCK, UNLOCK, BEGIN, COMMIT, ABORT.
Z nich budou složené všechny transakce. Pro snazší dokazování definujme jednoduché
transakce, které se z obyčejných dostanou takto:
1. BEGIN se vynechá
2. COMMIT se přepíše na UNLOCK všech zamčených dat
47
3. ABORT se přepíše na WRITE všech změněných dat následovaný UNLOCK všech
zamčených dat.
Tím se vlastně jen "rozepíší" akce při COMMITu a ABORTu, nic zlého se tedy nestane.
Operacemi WRITE u ABORTU se rozumí operace UNDO, které je potřeba provést, aby se
zrušily všechny viditelné efekty abortované transakce. Nyní můžeme definovat:
Transakce je dobře formovaná (well-formed), pokud jsou všechny akce pokryté
odpovídajícími zámky, a pokud je dodržené správné párování operací LOCK a UNLOCK.
Transakce je dvoufázová (two-phase) (Pozor! Neplést s dvoufázovým commitem!), pokud
jsou všechny operace LOCK před všemi operacemi UNLOCK.
Definujme historii (někdy se také používá označení rozvrh) jako posloupnost trojic
Historie popisuje možný způsob současného vykonání
zúčastněných transakcí.
<transakce, operace, data>.
Sériová historie (serial history) je historie, která vznikla prostým seřazením transakcí za
sebou. Taková historie je zjevně bezproblémová vzhledem k paralelismu.
Historie je legální (legal history), pokud popisuje proveditelný postup zamykání (tedy nejsou
v ní konflikty zámků podle tabulky kolizí zámků).
Definujme závislosti (dependencies) historie jako množinu trojic <T1, objekt, T2>, kde T2
bezprostředně závisí na hodnotě daného objektu zapsané T1, nebo kde T1 čte hodnotu
bezprostředně před její změnou T2 (formálně: existuje i, j takové, že T1 je v kroku i, T2 v
kroku j, v historii není zápis do objektu mezi kroky i a j, a operace T1[i] a T2[j] jsou buď
WRITE a WRITE, WRITE a READ, nebo READ a WRITE objektu).
Pokud mají dvě historie stejné závislosti, jejich transakce zjevně operují s týmiž verzemi dat,
a tedy skončí se stejným výsledkem – v takovém případě se řekne, že historie jsou
ekvivalentní.
Historie je izolovaná (isolated), pokud je ekvivalentní sériové historii. Takové historie nás
zjevně zajímají – nejsou v nich žádné cykly v grafu závislostí a tudíž ani žádné konflikty.
Pomocí závislostí lze definovat relaci "časového uspořádání" nad transakcemi: T1 <<< T2
právě když EX obj: <T1,obj,T2> IN DEP(H), nebo totéž tranzitivně. Polopaticky, T1 <<< T2
pokud T2 závisí na T1.
Hodí se ještě BEFORE(T): {TT, TT <<< T} a AFTER(T): {TT, T <<< TT} (což v podstatě
intuitivně vyjadřuje význam relace <<<).
A definujeme červí díru (wormhole) jako takový pár transakcí, kde T <<< TT a TT <<< T.
Zjevně wormholes porušují izolaci transakcí.
Zajímavá tvrzení:
48
1. Historie je izolovaná právě pokud neobsahuje wormholes (6.2.1 - Wormholes).
2. Pokud jsou transakce dobře formované a dvoufázové, jakákoliv legální historie z nich
sestavená bude izolovaná (6.2.2 - Locking).
3. Pokud transakce není dobře formovaná, nebo není dvoufázová, pak je možné k ní napsat
jinou dobře formovanou dvoufázovou transakci a sestavit historii s wormhole (kromě
degenerovaných transakcí) (6.2.3 - Converse Locking).
Důkazy těchto vesměs triviálních tvrzení jsou v následujících kapitolách.
6.2.1 Wormholes
Isolated => no wormholes: pokud je H izolovaná, je ekvivalentní nějaké sériové SH. V SH je
<<< částečné uspořádání, a protože SH a H mají stejné dependencies, platí to i v H. A protože
existence wormhole by implikovala existenci T <<< TT <<< T, došlo by ke sporu.
No wormholes => isolated: indukcí pro počet transakcí v historii. Pro N<2 triviální. Vezměme
teď H s N transakcemi a vybírejme z ní postupně transakce tak, aby platilo T1 <<< T2 <<< ...
– bez wormholes musíme někdy skončit (kdyby tam byla nějaká transakce dvakrát, tak by to
implikovalo wormhole), poslední transakci označme TT. Odeberme z H všechny operace v
TT, dostaneme HH. Zjevně
DEP(HH) je podmnožinou DEP(H) bez prvků s TT
(1)
Tedy HH nemá wormholes (protože je nemá H a DEP(HH) je subset DEP(H)). HH tedy
můžeme použít jako základ indukčního kroku a říci o ní, že je izolovaná, a existuje k ní SHH.
Když vytvořím SH přilepením TT za SHH, dostanu
DEP(SH) = DEP(SHH+TT) = DEP(SHH) or prvky s TT z DEP(H)
(2)
A dosazením DEP(HH) za DEP(SHH) do (1) (možné z definice) plus dosazením (1) do (2)
dostanu
DEP(SH) = DEP(HH) bez prvků s TT or prvky s TT = DEP(H)
Tedy H je izolovaná, protože je ekvivalentní se sériovou SH.
6.2.2 Locking
Předpokládejme, že H je historie dobře formovaných dvoufázových transakcí. Definujme
SHRINK(T) jako index prvního UNLOCK transakce T v historii H.
Drobné lemma: Pokud T <<< TT, pak SHRINK(T) < SHRINK(TT). Důkaz je triviální, pokud
T <<< TT tak existuje nějaký objekt, se kterým T i TT pracují, a alespoň jedna ho zapisuje (z
definice <<<). Pokud T zapisuje, pak T muselo provést RWLOCK, a aby TT mohlo
přistoupit, musí T nejprve provést UNLOCK, tedy alespoň jeden LOCK v TT je po UNLOCK
49
v T a protože všechny operace UNLOCK v TT jsou až za všemi operacemi LOCK, lemma
platí – pro zápis v TT obdobně.
A důkaz teorému – pokud by H nebyla izolovaná, existovala by wormhole, tedy posloupnost
T1 <<< T2 ... <<< T1, což vede na SHRINK(T1) < ... < SHRINK(T1), tedy spor.
6.2.3 Converse Locking
Definujeme degenerovanou (degenerated) transakci jako transakci, která buď zbytečně
zamyká něco co nepoužije, nebo která má nepárové LOCK a UNLOCK. Důkaz pak
pokračuje:
Ne-dobře formovaná => ne izolovaná: Existuje akce nad objektem nepokrytá zámkem. Pokud
udělám TT = RWLOCK, WRITE, WRITE, UNLOCK nad nepokrytým objektem a sestavím
historii tak, že nepokrytá akce bude mezi prvním a druhým write, vyrobil jsem wormhole.
Ne-dvoufázová => ne izolovaná: Existuje nějaký unlock A před lockem B. Udělám TT =
RWLOCK(A), RWLOCK(B), WRITE(A), WRITE(B), UNLOCK(A), UNLOCK(B) a v
historii umístím TT do místa mezi unlock a lock. Protože T není degenerovaná, musí před
unlockem něco dělat s A a po locku něco s B, tedy je v historii wormhole.
Proč je tohle důležité? Protože i když by vaše ne-dobře formovaná nebo ne-dvoufázová
transakce sama skončila dobře, je možné jí "zvenčí" poškodit přidáním jiné, což jistě není v
pořádku.
Pozor na směry implikací – isolovaná <=> neexistují wormholes, ale jen dobrá formovanost
& dvoufázovost => isolovaná historie (tedy existují transakce, na které jsou dobrá
formovanost v kombinaci s dvoufázovým zamykáním za určitých okolností zbytečně silné).
No a protože dobrá formovanost i dvoufázové zamykání implikují overhead, zjevně stojí za to
hledat jak je obejít, což si ukážeme v kapitole o izolačních levelech.
7. Unifikovaná teorie pro concurrency a recovery
Klasická teorie transakcí se zabývá zvlášť concurrency a zvlášť recovery. Teorie concurrency
zajišťuje správný paralelní běh několika transakcí za předpokladu, že nedojde k chybě. Teorie
recovery studuje zotavování se z případných chyb. Ačkoliv tyto dva aspekty transakcí nejsou
nezávislé, jsou studovány odděleně a definice jejich korektnosti jsou víceméně ortogonální. V
klasické teorii se všechno točí kolem serializovatelnosti rozvrhů a předpokladu, že nedojde k
chybě. V některých článcích je ukázáno, že existují rozvrhy, které jsou serializovatelné, ale
při výskytu chyby není možné data zrekonstruovat do původní podoby. V této kapitole se
seznámíme s takovým přístupem ([2]) ke concurrency control, kde se zohlední i aspekty
recovery. Kromě klasického read/write modelu se podíváme na sémanticky bohaté operace.
7.1 Tradiční pohled jinými slovy
Osvěžíme si některé pojmy a upřesníme si jejich definice:
50
− Databáze je soubor objektů, se kterými se může manipulovat pomocí operací, které jsou
součástí nějakých transakcí.
− Transakce Ti je částečně uspořádaná množina operací relací <i, poslední operací je vždy
buď abort nebo commit (v rozvrzích dále značíme jen a nebo c). Abort zruší všechny
změny provedené transakcí a commit všechny změny uloží v databázi. Transakce, která
není abortována ani commitována je aktivní. Poznámka: Někoho možná zarazí, že operace
jsou v transakci částečně uspořádané a nikoliv uspořádané. Jde o to, že uspořádanost
vyžadujeme jen u operací manipulujících s daty (read, write), zatímco v transakci jsou
ještě jiné operace u kterých nám stačí jenom částečná uspořádanost (všechny unlock za
posledním lock v případě dvoufázového zamykání) nebo se jimi vůbec nezabýváme.
− Operace jsou konfliktní, pokud patří různým transakcím, manipulují se stejným objektem
v databázi a jedna z nich je write.
− Historie (nebo rozvrh) je částečné uspořádání S s relací <S operací v transakcích patřících
do tohoto rozvrhu; uspořádání každé transakce je podmnožinou uspořádání rozvrhu,
neboli <i ⊂ <S. Všechny konfliktní operace v S jsou <S setříděné.
− Rozvrh je sériový, pokud se neprokládají operace jednotlivých transakcí, tedy pro každé
dvě transakce Ti a Tj platí, že pro každé dvě operace oi ∈ Ti, oj ∈ Tj platí oi <S oj.
− Dva rozvrhy jsou konflikt-ekvivalentní, pokud jsou definovány nad stejnými transakcemi a
operacemi a mají stejnou množinu konfliktních operací.
− Commitovaná projekce rozvrhu obsahuje pouze transakce, které končí operací commit.
− Rozvrh je serializovatelný (SR), pokud jeho commitovaná projekce je konfliktekvivalentní nějakému sériovému rozvrhu. Je známo, že rozvrh je serializovatelný, právě
když jeho závislostní graf je acyklický.
− Transakce Ti čte x z jiné transakce Tj, pokud wj(x) <S ri(x) a neexistuje wk(x) takové, že
wj(x) <S wk(x) <S ri(x).
− Rozvrh je recoverable (RC), pokud pro každé dvě transakce Ti a Tj takové, že Ti čte x z Tj
a Ti commituje, pak Tj commituje před Ti.
− Rozvrh předchází kaskádovým abortům (ACA), pokud pro každé dvě transakce Ti a Tj
takové, že Ti čte x z Tj, pak cj <S ri(x).
− Rozvrh je striktní (ST), pokud kdykoliv wj(x) <S oi(x), i ≠ j, buď aj <S oi(x) nebo cj <S
oi(x), kde oi(x) je ri(x) nebo wi(x).
− Rozvrh je rigorózní (RG), pokud je striktní a navíc žádný objekt nemůže být přepsán,
dokud transakce, která ho předtím četla, neskončí (tj. provede commit nebo abort), tj.
když rj(x) <S wi(x), i ≠ j, pak buď aj <S wi(x) nebo cj <S wi(x).
− Platí RG ⊂ ST ⊂ ACA ⊂ RC. Třída SR není s těmito třídami porovnatelná a pouze se ví,
že RG ⊂ SR.
7.2 Stručný úvod do semantics-based concurrency control
Co je semantics-based concurrency control? Místo abychom se dívali na data optikou operací
read a write, díváme se na ně jako na objekty, s nimiž pracují sémanticky bohaté operace. Pak
se zavádějí pojmy jako komutativita operací (v klasickém read/write modelu komutuje jen
read sám se sebou), operace se rozdělují na observers, které jen čtou data z objektu, a
modifiers, které modifikují objekty.
51
Tento způsob pohledu je výhodný, protože identifikace komutujících operací umožní snížit
počet zámků a tím zvýší efektivitu a propustnost systému.
7.3 Unifikovaný model
Unifikovaný model transakcí navržený v [2] stojí na těchto základních principech:
− Operace jsou definovány na abstraktních datových typech (ADT) a ke každé do operaci
existuje undo operace, která vrátí zpět efekty způsobené operací do.
− Všechny operace související se zotavením (recovery) jsou v transakci přítomné (tedy i v
rozvrhu) ve formě příslušných undo operací.
− Serializovatelnost rozvrhů se zkoumá vzhledem ke komutativitě do a undo operací, neboli
zkoumá se současně schopnost zotavení.
7.3.1 Operace
Databáze DB je množina abstraktních datových typů D a množina operací O nad těmito
datovými typy (říkame jim forward operace). Existují dvě speciální operace – commit a abort
(značíme c a a).
Pro každou operaci o (vyjma c a a) existuje undo nebo backward operace o-1, tedy existuje
množina O-1 všech undo operací k operacím z O. Operace vracejí hodnotu, která je funkcí
změny, takže např. read(x) vrací hodnotu x a write(x) vrací hodnotu, která byla přepsána
hodnotou x. α = p1p2p3…pn je sekvence operací. Undo operace může následovat jedině za
příslušnou do operací. Říkáme, že sekvence operací nad O ∩ O-1je dobře formovaná, pokud
každá undo operace následuje až za příslušnou do operací.
Pokud je s0 iniciální stav databáze, pak každý další stav s je generován sekvencí operací α,
neboli s = s0α. Dva stavy s1 = s0α1a s2= s0α2 jsou ekvivalentní, právě když pro každou dobře
formovanou sekvenci operací β jsou návratové hodnoty operací z β aplikované na s1stejné
jako když jsou aplikované na s2.
Sekvence operací σ je effect-free, pokud pro všechny sekvence α a β takové, že sekvence ασβ
i αβ jsou dobře formované, platí že návratové hodnoty operací v sekvenci β jsou stejné v
sekvenci ασβ a v sekvenci αβ.
Pak se snadněji řekne, co je to undo operace k operaci o: je to taková operace o-1, že sekvence
oo-1je effect-free. Undo operace je závislá na příslušné do operaci, protože jako jeden ze
svých vstupů má výsledek do operace.Třeba undo k operaci write potřebuje výsledek operace
write – tedy původní hodnotu, která byla operací write přepsána. Speciálním případem effectfree sekvencí jsou sekvence s jedinou operací; taková operace zjevně nemění stav databáze
(třeba operace read). Undo operací k takové operaci je null (značíme λ), která je taky effectfree a nedělá nic.
Jak jsme řekli, undo operace nemohou být použity jako dopředné operace, slouží jen k anulaci
efektů dopředných operací. Pak ale není nutné, aby měly undo operace nějaké návratové
52
hodnoty a dále budeme předpokládat, že všechny o-1 vracejí konstantu 0, pouze λ vrací
konstantu null.
7.3.2 Komutativita
Dvě operace p a q z O ∩ O-1 komutují, právě když pro všechny sekvence α a β z O ∩ O-1
takové, že αpqβ a αqpβ jsou dobře formované, jsou návratové hodnoty operací z β v sekvenci
αpqβ stejné jako v sekvenci αqpβ.
Zjevně null operace komutují se všemi operacemi. Příklad s tradičními operacemi read a write
je v následující tabulce.
read
write
read-1
write-1
read
+
-
+
-
write
-
-
+
-
read-1
+
+
+
+
write-1
-
-
+
-
Tabulka 1: Tabulka komutativity pro klasický read/write model
Plus je tam, kde operace komutují. V obecném případě operací se samozřejmě uvažují
konflikty vzhledem ke všem parametrům operací. Read(x) vrací aktuální hodnotu x,
write(x,v) mění hodnotu objektu x na v a vrací původní hodnotu x, read-1je λ operace, která
vrací null, write-1(x, v) zapíše původní hodnotu objektu x a vrací 0. V tabulce vidíme jednu
speciální vlastnost read/write modelu: pokud dvě operace nekomutují, pak nekomutují ani se
svými undo operacemi, ani jejich undo operace vzájemně. Pokud naopak dvě operace
komutují, pak všechny ostatní kombinace těchto dvou operací a jejich undo operací také
komutují. Touto vlastností – perfektností – se ještě budeme zabývat.
Jinak vypadá tabulka komutativity sémanticky bohatších operací:
insert
delete
test
insert-1
delete-1
test-1
insert
-
-
-
-
-
+
delete
-
-
-
-
-
+
test
-
-
+
-
-
+
insert-1
-
-
-
+
-
+
delete-1
-
-
-
-
+
+
test-1
+
+
+
+
+
+
Tabulka 2: Tabulka komutativity sémanticky bohatých operací
Insert(x) vloží objekt x do množiny S. Pokud už tam objekt je, operace neudělá nic a vrátí 0,
jinak vrátí 1. Insert-1(x) smaže objekt x z množiny S, pokud je ale x = 0 (do operace insert
53
byla neúspěšná, protože x už byl v S) neudělá nic. Vždy vrací 0. Delete(x) smaže objekt x z
množiny S; pokud tam x není, neudělá nic a vrátí 0, jinak vrátí 1. Delete-1(x) vloží element
zpět do S, pokud je x = 0 (delete(x) byla neúspěšná, protože x v S nebyl), tak neudělá nic.
Vždy vrací 0. Test(x) vrací 1 pokud je x v množině S, jinak vrací 0. Test-1(x) je λ operace,
která vrací null. Vidíme, že relace komutativity na těchto operacích není perfektní, např.
Insert nekomutuje sám se sebou, ale Insert-1 komutuje sám se sebou. Podobně s delete – obě
tyto operace nejsou efect-free.
7.3.3 Rozvrhy
Transakce a rozvrh jsou definovány stejně jako v klasické teorii, pouze mluvíme o obecných
operacích z O místo pouze o operacích read a write. Navíc, v rozvrhu může být operace a (Ti1,
…, Tik), což je hromadný abort, neboli abort je proveden pro každou z transakcí Ti1, …, Tik a
na pořadí těchto abortů nezáleží. a(Ti) = ai.
Dvě operace p a q jsou konfliktní, právě když p a q nekomutují a patří různým transakcím.
Je zjevné, že tato definice neodporuje naší neformální definici z kapitoly 7.1, pouze zavádí
sémanticky bohaté operace.
Rozvrh je serializovatelný pokud jeho commitovaná projekce je konflikt-ekvivalentní
nějakému sériovému rozvrhu.
7.3.4 Rozšířené rozvrhy
Kritérium serializovatelnosti nepostihuje abortované transakce. Abychom postihli i
abortované transakce, nahradíme každý abort sekvencí undo operací, které provedou undo
celé transakce, a transakci ukončíme commitem.
Nechť S = (A, <S) je rozvrh, kde A množina operací a <S je částečné uspořádání těchto
operací. Rozšířením rozvrhu budeme nazývat takový rozvrh S' = (A', <S'), že:
1. A' je množina operací odvozených z A takto:
− Pro každou transakci Ti ∈ A, pokud oi ∈ Ti a oi není abort, pak oi ∈ A'.
− S aktivními transakcemi je nakládáno jako s abortovanými, jsou abortovány přidaným
skupinovým abortem a(Ti1, …, Tik), který je maximálním prvkem A'.
− Pro každou abortovanou transakci Tj ∈ A a všechny operace oj ∈ Tj existují undo operace
oj-1 ∈ A'. Abort operace aj ∈ A je změněn na c ∈ A'. Operace a(Ti1, …, Tik) je nahrazena
sekvencí ci1, …, cik.
2. Částečné uspořádání <S' je definováno následujícím způsobem:
− Pro každé dvě operace oi a oj, pokud oi <S oj, pak oi <S' oj.
− Když transakce Ti a Tj abortují v S, a jejich aborty nejsou uspořádané relací <S, pak každé
dvě konfliktní undo operace transakce Ti jsou uspořádané <S' v opačném pořadí než jejich
příslušné do operace v S. Pokud do operace nejsou uspořádané relací <S, pak jsou tyto dvě
undo operace v libovolném pořadí, neboli nejsou uspořádané <S'.
54
− Všechny undo operace transakce Ti, která necommituje v A, následují původní operace z
transakce a musí předcházet před commitem transakce Ti podle relace <S'.
− Pokud on <S a(Ti1, …, Tik) a některá undo operace oj-1 (j ∈ i1, …., ik) je konfliktní s on, pak
on <S' oj-1.
− Když ai <S aj pro i ≠ j a oi-1 je konfliktní s oj-1, pak oi-1 <S' oj-1.
7.3.5 RED a PRED
Rozvrh je reducibilní (RED), pokud k němu existuje alespoň jeden rozšířený rozvrh S'
takový, že může být transformován do serializovatelného rozvrhu pomocí jednoho ze dvou
následujících pravidel:
1. Pravidlo komutativity: Když jsou oi a oj operace v S' z různých transakcí takové, že oi
<S' oj, oi komutuje s oj a neexistuje žádná operace p taková, že oi <S' p <S' oj, pak může být
uspořádání oi <S' oj nahrazeno uspořádáním oj <S' oi.
2. Pravidlo undo: Když jsou o a o-1 operace v S' takové, že neexistuje žádná operace p
taková, že oi <S' p <S' oj, pak mohou být o a o-1 vymazány z rozvrhu S'.
Poznámka: V rozšířených rozvrzích tedy nejsou operace abort, všechny transakce jsou
commitovány. Pravidla 1 a 2 platí pro všechny operace kromě operace commit.
Příklad: Uvažujme rozvrh S1: delete1(x) insert2(x) test3(x) c2 a3. Jeho expanze v S1': delete1(x)
insert2(x) test3(x) c2 test3-1(x) c3 delete1-1(x) c1 není reducibilní, protože delete1(x) <S' insert2(x)
<S' delete1-1(x), takže je v rozšířeném rozvrhu cyklická závislost, které se nelze zbavit. Rozvrh
S2: delete1(x) insert2(x) test3(x) c2 c1 a3 s expanzí S2': delete1(x) insert2(x) test3(x) c2 c1
test3-1(x) c3 je reducibilní, protože v rozšířeném rozvrhu není cyklická závislost.
Transakční manager dynamicky generuje rozvrhy spouštěných transakcí. To znamená, že
rozvrh může obsahovat ještě necommitované transakce o nichž nevíme, zda budou
commitovat, takže by měl být serializovatelný i každý prefix celého rozvrhu. To znamená, že
reducibilita rozvrhu by měla být uzavřena vzhledem k prefixům, což bohužel není, takže
nemůže být použita pro on-line rozvrhování transakcí.
Rozvrh S = (A, <S) je prefix reducibilní (PRED) pokud je každý prefix S reducibilní.
Příklad: S2 z předchozího příkladu není prefix reducibilní (protože prefix delete1(x) insert2(x)
test3(x) c2 není reducibilní). Oproti tomu rozvrh S3: delete1(x) insert2(x) test3(x) c1 c2 a3 je
prefix reducibilní. Třídu prefix reducibilních rozvrhů považujeme za třídu rozvrhů
zajišťujících atomicitu transakce a serializovatelnost transakcí.
7.4 Unifikovaný model pro READ/WRITE operace
7.4.1 Vztahy ke klasické teorii
Klasické kritérium striktnosti (SR-ST v kombinaci se serializovatelností) a recoverability
(SR-RC v kombinaci se serializovatelností) reflektují také intuitivní chápání korektního
zotavení (recovery), zřejmý další krok je zkoumat vztahy mezi kritérii RED a PRED k SR-ST
55
a SR-RC. Ukázali jsme, že PRED je vhodné kritérium které sjednocuje intuitivní představu
korektního concurrency control a recovery.
Je zjevné, že platí PRED ⊂ RED. Dále platí RG ⊂ SR-ST ⊂ PRED (důkaz a i další důkazy se
najdou v [5] a jiných článcích na podobné téma od stejných autorů). Z toho plyne, že PRED
pokrývá klasické kritérium korektnosti a připouští více rozvrhů než toto klasické kritérium.
Teď je potřeba ukázat, že PRED obsahuje jenom intuitivně správné rozvrhy.
Platí PRED ⊂ SR-RC. Protože SR-RC je zkonstruována tak, že obsahuje všechny intuitivně
přijatelné rozvrhy, jsou i všechny rozvrhy z PRED akceptovatelné. PRED vylučuje takové
rozvrhy, které nemůžou být korektně zpracovány pomocí log-based metod zotavení.
Celkově se tedy ukazuje, že PRED vyhovuje jak kritériím concurrency control, tak kritériím
recoverability. Dalším krokem bude nalézt třídu, která by mohla být efektivně dynamicky
generována plánovačem transakcí s rozumnou složitostí.
7.4.2 PRED = SOT
Rozvrh S je SOT (serializable with ordered termination) pokud je
1. serializovatelný,
2. recoverable a pro každou dvojici konfliktních operací wi(x) <S wj(x), wj(x) <S ti, ti ∈{a,c}
platí, že
− když Tj(x) commituje, pak commituje po Ti(x)
− když Ti abortuje, pak abortuje poté co abortuje Tj, nebo S obsahuje skupinový abort a(...,
Ti, ..., Tj, ...).
Výše uvedená definice má dvě části. První z nich je o serializovatelnosti – tedy garantuje
korektní provedení commitovaných transakcí v rozvrhu. Druhá část – o isoloted recovery –
zavádí restrikce toho, v jakém pořadí mohou transakce skončit. Tyto restrikce se použijí jen
pokud nastává konflikt v read/write modelu, tj. jeden z konfliktů rw, ww, wr.
Zkoumejme nyní rw konflikt. Uvažujme rozvrh S = r1(x) w2(x) a rozvrh S', který obsahuje S a
dvě možná ukončení transakcí T1 a T2. Snadno nahlédneme, že všechny možné rozvrhy S'
jsou prefix reducibilní, protože undo operací operace read je operace null, která komutuje s
každou operací. Takže nemusíme použít v případě rw konfliktů žádné restrikce.
Zkoumejme tedy stejným způsobem ww a wr konflikty - k tomu použijeme rozvrh S1 = w1(x)
w2(x) a rozvrh S2 = w1(x) r2(x). Tabulka 3 ukazuje všechny možné kombinace ukončení
transakcí T1 a T2 spolu s údajem, zda jsou rozvrhy prefix reducibilní.
S1 = w1(x) w2(x)
S2 = w1(x) r2(x)
1
w1(x) w2(x) a1 a2 ∉ PRED
1
w1(x) r2(x) a1 a2
∈ PRED
2
w1(x) w2(x) a1 c2 ∉ PRED
2
w1(x) r2(x) a1 c2
∉ PRED
3
w1(x) w2(x) c2 c1 ∉ PRED
3
w1(x) r2(x) c2 c1
∉ PRED
56
4
w1(x) w2(x) c2 a1 ∉ PRED
4
w1(x) r2(x) c2 a1
∉ PRED
5
w1(x) w2(x) a2 a1 ∈ PRED
5
w1(x) r2(x) a2 a1
∈ PRED
6
w1(x) w2(x) a2 c1 ∈ PRED
6
w1(x) r2(x) a2 c1
∈ PRED
7
w1(x) w2(x) c1 c2 ∈ PRED
7
w1(x) r2(x) c1 c2
∈ PRED
8
w1(x) w2(x) c1 a2 ∈ PRED
8
w1(x) r2(x) c1 a2
∈ PRED
Tabulka 3: Možné kombinace ukončovacích operací rozvrhů S1 a S2
Protože v případech 2, 3 a 4 není rozvrh S2 prefix-reducibilní, aplikujeme na wr konflikty
restrikce týkající se recoverability; rozvrhy 2, 3 a 4 totiž nejsou recoverable. Podobně u
rozvrhu S1 nejsou možnosti 1 – 4 prefix reducibilní, proto v případě ww konfliktů zavádíme v
definici SOT podmínky z bodu 2 definice SOT, které tyto rozvrhy nepovolují.
Z toho plyne, že SOT = PRED. Definice PRED je dána rekurzivně, což brání jejímu použití v
dynamických plánovačích. Oproti tomu, nová definice SOT je ryze procedurální, což vede k
rozšíření tradičního testovaní grafu serializovatelnosti, dvoufázovým uzamykacím
protokolům a time-stamp ordering protokolům, takže nové protokoly zajišťují jak atomicitu
tak serializovatelnost.
7.5 Unifikovaný model pro sémanticky bohaté operace
Nyní se pokusíme zobecnit závěry o prefix reducibilních rozvrzích pro modely se sémanticky
bohatými operacemi. Uvedeme si podmínky, za kterých je SOT opět použitelná – i v obecném
případě můžeme dynamicky testovat prefix-reducibilnost, bohužel s větší složitostí výpočtu
(polynomiální), která brání použití pro konstrukci dynamických plánovačů. Proto budeme
definovat podtřídu třídy prefix reducibilních rozvrhů, která umožní snadnější konstrukci
protokolů.
7.5.1 Reducibilní rozvrhy
Definice reducibilních rozvrhů v 7.3.4 není konstruktivní. V této kapitole si ukážeme
konstruktivní proceduru která rozhodne zda daný rozvrh je či není reducibilní. Uvažujme pár
operací (oi, oi-1) v rozšířeném rozvrhu S'. Když mezi oi a oi-1 nejsou žádné jiné operace, tak
může být tato dvojice z S' vypuštěna pomocí undo pravidla. Nyní předpokládejme, že mezi oi
a oi-1 nějaké operace jsou. Nechť o1, ..., on jsou mezi oi a oi-1 takové, že každé ok je konfliktní
s ok+1, k ∈{1, ..., n-1}, oi je konfliktní s o1 a ok je konfliktní s oi-1. Pak, abychom mohli
eliminovat dvojici (oi, oi-1), musíme přerušit řetěz operací v rozvrhu pomocí eliminace alespoň
jedné operace z rozvrhu pomocí pravidla undo nebo pravidla komutativity. Jenže pokud každá
z operací ok v rozvrhu patří do commitované transakce, pak nemůže být žádná z nich z S'
eliminována. V takovém případě S není reducibilní. Tedy, potřebujeme pro každou dvojici (oi,
oi-1) zjistit, zdali ji lze eliminovat z rozšířeného rozvrhu S'.
Nechť S je rozvrh a S' jeho rozšíření. Pro popis reducibility rozvrhu S vytvoříme graf
reducibility RG(S') následujícím způsobem: Uzly grafu jsou všechny operace z S'. Pokud oi
∈ Ti <S – předchází oi z Tj (i ≠ j) a oi je konfliktní s oj, pak RG(S') obsahuje hranu <oi, oj>.
57
Platí: Dvě operace oi, oi-1 mohou být vypuštěny z rozšířeného rozvrhu S' pomocí pravidla
komutativity právě když neexistuje mezi nimi cesta v RG(S').
Když vyjdeme z předcházejícího tvrzení, můžeme rozhodnout o reducibilitě daného
rozšířeného rozvrhu S' pomocí následující procedury:
1. Pro S' sestroj RG(S').
2. Najdi v RG(S') dvojici uzlů oi a oi-1 takovou, že mezi nimi není cesta.
3. Když taková dvojice neexistuje a S' obsahuje některé zpětné operace, pak není rozvrh
S reducibilní a procedura končí. Pokud dvojice neexistuje a S' neobsahuje zpětné operace,
pak procedura končí.
4. Když taková dvojice existuje, smaž dvojici uzlů spolu se všemi incidenčními hranami
z RG(S') a smaž dvojici z S'.
5. Pokračuj podle 2.
Pokud jako výsledek procedury získáme serializovatelný rozvrh s pouze dopřednými
operacemi, pak je S reducibilní. V opačném případě není S reducibilní.
Příklad: Uvažujme rozvrh S3: insert1(x) delete2(x) insert3(x) a1 a2 a3. Jeho rozšířením je S3' =
insert1(x) delete2(x) insert3(x) insert1-1 (x) delete2-1 (x) insert3-1 (x) c1 c2 c3. Pokud bereme
postupně všechny dvojice po sobě jdoucích operací, jsou vždy konfliktní. Graf reducibility
pro S3' obsahuje cestu (insert1(x) delete2(x) insert3(x) insert1-1 (x) delete2-1 (x)), což je cesta
mezi dopřednou a k ní zpětnou operací a tudíž není S3 reducibilní.
Jiný příklad: Uvažujme rozvrh S4: delete1(x) delete2(x) delete3(x) a1 a2 a3. Jeho rozšířením je
S4' = delete1(x) delete2(x) delete3(x) delete1-1 (x) delete2-1 (x) delete3-1 (x) c1 c2 c3. delete1(x) je
konfliktní s delete2(x), delete2(x) je konfliktní s delete3(x), delete3(x) je konfliktní s delete1-1.
Graf reducibility S4' neobsahuje cestu mezi delete3(x) a delete(x)3-1 protože delete(x)-1
komutuje sama se sebou. delete3(x) a delete(x)3-1 tedy mohou být eliminovány z grafu. Zbylé
dva páry (delete1(x) a delete(x)1-1 delete2(x) a delete(x)2-1) mohou být z grafu eliminovány
podobně. Proto je S4 reducibilní.
Graf reducibility může být sestrojen v O(n2), kde n je počet operací v S'. Testování, zda
existuje cesta z oi do oi-1 trvá O(n2) kroků. Test se musí provést pro nejvýše n dvojic. Kroky 2,
3 a 4 z výše uvedené procedury musí být opakovány v nejhorším případě n-krát. Celková
složitost je tedy O(n4), což je relativně hodně. V následující kapitole si ukážeme praktičtější
procedury s nižší složitostí, které umožní nalézt podtřídy reducibilních rozvrhů.
7.5.2 PRED ≠ SOT
Definujme SOT pro množinu sémanticky bohatých operací O: Rozvrh S je SOT (serializable
with ordered termination) pokud je serializovatelný a pokud pro každé dvě transakce Ti a Tj a
každé dvě operace oi ∈ Ti, oj ∈ Tj, takové, že oi <S oj, ai nepředchází oj v S, oi je konfliktní s
oj a oi-1 je konfliktní s oj a platí:
1. Když Tj commituje v S pak Ti také commituje v S a ci <S cj.
2. Pokud je oi-1 je konfliktní s oj-1 a Ti abortuje v S pak Tj také abortuje v S a buď aj <S ai
nebo a(...,Ti, ..., Tj, ...) ∈ S.
58
První podmínka zajistí, že transakce commitují v pořadí svých konfliktních operací. Druhá
podmínka zajistí, že operace abort konfliktních transakcí budou provedeny v opačném pořadí
než je pořadí jejich konfliktních operací. Obě podmínky jsou potřeba, aby byl rozvrh prefix
reducibilní. Dříve jsme pro read/write model ukázali, že tyto podmínky jsou nutné a
postačující pro zajištění prefix reducibility rozvrhu. Zde ukážeme, že jsou to postačující
podmínky i pro libovolnou množinu sémanticky bohatých operací O.
Platí PRED ⊂ SOT. Aby byl rozvrh SOT, nestačí, že je prefix reducibilní. Buď se musíme
spokojit z procedurou z kapitoly 7.5.1, nebo musíme provést restrikci třídy PRED nebo
musíme provést restrikci relace komutativity. Oba přístupy si ukážeme v následujících dvou
kapitolách.
7.5.3 Bezpečné rozvrhy
Pro zjištění, že daný rozvrh je prefix reducibilní, je nutné eliminovat všechny páry
dopředných a zpětných operací pomocí pravidla undo. Je možné posunovat dopřednou
operaci dopředu směrem k její zpětné operaci, nebo naopak posouvat zpětnou operaci dozadu
směrem k dopředné operaci. Obtížnosti s konstrukcí PRED plynou právě z tohoto faktu spolu
s tím, že dopředná a zpětná operace mohou mít různou množinu komutujících operací.
Abychom PRED omezili na množinu, se kterou může plánovač nebo recovery manager
efektivně pracovat, uvažujme, co se stane, když je naplánována zpětná operace. Účelem
zpětných operací je provést vrácení všech efektů nějaké dopředné operace. Uvažujme rozvrh,
kde po operacích o1, ..., ok musí být provedena operace o1-1. Aby byla zajištěna konzistence,
plánovač musí zajistit, aby naplánovaní o1-1 neovlivní operace o2, ..., ok. Toho může být
dosaženo dvěma způsoby:
− Operace o2, ..., ok nejsou konfliktní s o1 a tedy jejich efekt není ovlivněn návratovou
hodnotou o1. Pak může o1 být přesunuta k o1-1 a celá dvojice může být odstraněna
z rozvrhu pomocí pravidla undo.
− Operace o1-1 komutuje s každou operací z o2, ..., ok. Pak může o1-1 být přesunuta k o1 a
celá dvojice může být odstraněna z rozvrhu pomocí pravidla undo.
Tyto dvě možnosti jsou formálně zavedeny v následující definici:
Rozvrh je dopředně bezpečný (forward safe, FSF) (zpětně bezpečný (backward safe, BSF))
právě když pro každé dvě transakce Ti a Tj a každé dvě operace oi ∈ Ti, oj ∈ Tj, takové, že oi
<S oj, ai nepředchází oj v S, oi (oi-1) je konfliktní s oj platí:
1. Když Tj commituje v S pak Ti také commituje v S a ci <S cj.
2. Pokud Ti abortuje v S a oj-1 ≠ λ, pak Tj také abortuje v S a buď aj <S ai nebo a(...,Ti, ..., Tj,
...) ∈ S.
Existují dopředně bezpečné rozvrhy, které nejsou zpětně bezpečné a naopak. Příklad:
Uvažujme rozvrh S1: test1(x) insert2(x) c2 a1. Protože insert(x) je konfliktní s test(x), ale
test1-1(x) komutuje s insert(x), rozvrh je zpětně bezpečný, ale není dopředně bezpečný.
Protože pořadí commitů závisí na pořadí konfliktních dopředných a zpětných operací místo
jen pořadí konfliktních dopředných operací, nejsou zpětně bezpečné rozvrhy vždy
59
serializovatelné. Např. S: test1(x) insert2(x) test2(x) insert1(x) c1 c2 je zpětně bezpečný, ale
není serializovatelný. Naopak, každý dopředně bezpečný rozvrh je serializovatelný.
Třída dopředně bezpečných rozvrhů FSF a třída serializovatelných zpětně bezpečných
rozvrhů BSF-SR jsou vlastními podtřídami PRED, neboli FSF ⊂ PRED, BSF-SR ⊂ PRED.
7.5.4 Perfektní komutativita
Zaměřme se na důsledky toho, že komutativita nemusí být vždy symetrická vzhledem
k dopředným a zpětným operacím. Zavedeme pojem perfektnosti, která vyžaduje, aby pokud
nejsou některé kombinace dopředných a zpětných operací komutativní, pak nejsou
komutativní žádné kombinace.
Relace komutativity je perfektní, pokud pro každé dvě operace p a q buď pα komutuje s qβ pro
všechny možné kombinace α, β ∈ {-1, 1} nebo pα nekomutuje s qβ pro všechny možné
kombinace α, β ∈ {-1, 1} s výjimkou zpětných operací λ, které komutují se vším.
Perfektnost je zajištěna v read/write modelu, protože undo operací k write je jiný write.
Protipříkladem je např. operace insert, která sama se sebou nekomutuje, zatímco insert-1 sám
se sebou komutuje.
Platí: Nechť je relace komutativity perfektní. Pak. je rozvrh prefix reducibilní právě když je
SOT.
Půvab modelů s perfektní komutativitou spočívá v tom, že jsou "izomorfní" s read/write
modelem. Všechny protokoly zajišťující atomicitu a serializovatelnost pro read/write model
mohou být totiž pro tyto modely použity.
7.6 Shrnutí
Pro read/write model je nejdůležitější, že se našla množina SOT, která je ekvivalentní s PRED
a přitom je definována procedurálně a lze použít pro konstrukci dynamických plánovačů. Pro
obecný model se sémanticky bohatými operacemi se nám podobného výsledku dosáhnout
nepodařilo. Ukázalo se, že vše záleží na nesymetričnosti komutativity pro dopředné a zpětné
operace. Pokud jsme tedy pro SOT použili perfektní komutativitu, dosáhli jsme stejného
výsledku jako pro read/write model. Obecně nám vyšly jako přijatelné dopředně bezpečné
rozvrhy a serializovatelné zpětně bezpečné rozvrhy. Možná je tato teorie receptem na lepší
definici multilevel transakcí, kde by se možná nemělo přistupovat k concurrency control a
recovery odděleně, ale jednotně a v souladu.
8. Zámky
8.1 Stupně izolovanosti
S izolací je jeden problém: sice se ví postačující podmínky, ale ty jsou v řadě situací příliš
omezující (když chce někdo udělat průměr všech položek ve velké databázi, podle minule
60
uváděných zásad by prostě postupně zamknul celou databázi). Takže se hledají kompromisy totiž jak zamykat, aby výsledek byl stále ještě dost izolovaný a přitom nebrzdil.
Jedno elegantní řešení se nazývá degrees of isolation (elegantní je na něm korespondence
mezi "what you do" a "what you get"):
− Stupeň 0: Transakce nepřepíše cizí data, pokud ta jsou součástí transakce alespoň stupně
1, tj. zamykání je dobře formované s ohledem na zápis ale není dvoufázové.
− Stupeň 1: Transakce nebude mít lost updates - zamykání je dvoufázové s ohledem na
exkluzivní zámky a dobře formované s ohledem na zápis.
− Stupeň 2: Transakce nebude mít lost updates ani dirty reads - zamykání je dvoufázové s
ohledem na exkluzivní zámky a dobře formované.
− Stupeň 3: Transakce bude úplně izolovaná - zamykání je dvoufázové a dobře formované.
Pěkné je to, že uvedené stupně izolace se dají kombinovat a přitom mají stále zaručenou
funkci. Formálně se to dá říci třeba takto: transakce s lock protokolem stupně 0, 1, 2 nebo 3
bude mít v jakékoliv legální historii stupeň izolace 1, 2 nebo 3 za předpokladu, že všechny
ostatní transakce v historii mají lock protokol stupně alespoň 1.
Samozřejmě, že u stupňů izolace menších než 3 je potřeba dávat pozor na to co se děje například transakce s lock protokolem stupně 0 a 1 mohou číst dirty data, a pokud podle nich
něco zapíší, nemusí to být správně. Řešení: buď se řekne že člověk musí vědět co dělá, nebo
se stupně 0 a 1 dovolí pouze v read-only transakcích.
8.1.1 Jak je to v SQL?
Necháme na laskavé úvaze čtenářů, jak by se asi choval stupeň izolovanosti 3 při select
balance into :my_balance from bank where account_id = :my_account_id. SQL2
proto definuje "set transaction isolation level":
−
−
−
−
read uncommitted (stupeň 1),
read committed (stupeň 2, cursor stability),
repeatable read (stupeň 3 bez phantom protection),
serializable (stupeň 3).
Kupodivu default často není serializable ale read committed. Takže například následující kód
je špatně:
exec sql select bal into :my_bal from bank where id = :my_id;
my_bal += 10;
exec sql update bank set bal = :my_bal where id = :my_id;
Zatímco následující kód je dobře:
exec sql declare cursor c for select bal from bank where id = :my_id;
exec sql open c;
exec sql fetch c into :my_bal;
61
my_bal += 10;
exec sql update bank set bal = :my_bal where current of cursor c;
exec sql close c;
Teď by asi bylo dobře zamyslet se nad dalšími detaily kolem stupňů izolovanosti. Zajímavé
body jsou:
− K čemu je který level: Level 0 se při read-only transakcích neliší od level 1, při
read/write je nebezpečný protože nemusí jít udělat abort, uvažuje se, protože představuje
minimum které neohrožuje ostatní, dobrý je tak na maintenance utilities, level 1 je prima
pro browsing a sbírání statistik, tam totiž dirty data nemusí vadit, level 2 a 3 už jsou
celkem bezpečné.
− Jak hodně který level zatěžuje systém: Člověk by čekal že zátěž bude růst v pořadí od
level 0 do level 3, ale to nemusí být vždy pravda - vyšší levely sice více blokují, ale méně
zamykají, tedy se šetří locking overhead.
− Drobné optimalizace: Zmínili jsme vhodnost level 1 pro statistiky, ale ono to není tak
úplně pravda, protože dirty data mohou statistiky dost rozhodit. Tomu má zabránit read
past – totiž data se při čtení sice nezamykají, ale pokud je na nich exkluzivní zámek (a
tedy mohou být dirty), přeskakují se (to se mimochodem hodí nejen při level 1). Notify
lock je triviální, například "notify from bank where name = "ASDF"" se vrátí jakmile se v
bance objeví záznam se jménem ASDF.
8.2 Fantomy a predikátové zámky
Nejdříve zmínka o tom, čemu se říká phantoms. Obecně vzato, každá operace, která něco dělá
nad celou databází, by jí měla celou zamknout. Ale když si člověk uvědomí, že takovou
operací je třeba i "select bal from bank where id = :my_id", protože obsahuje predikát nad
celou databází (id = :my_id), není to ten nejlepší přístup. Snahou je zamykat pouze jednotlivé
záznamy (v případě selectu jen ty, které byly vybrány), pak je ale problém co když přibude
nový záznam splňující podmínku selectu. A to je právě záznam, kterému se (mimo jiné – viz
delete) říká fantom.
Nabízí se přímočaré (a tedy nepoužitelné) řešení, totiž zamykat podle predikátů (tedy říci
například "lock bank where id = :my_id"). To zní možná na první pohled jako dobrý nápad,
ale vznikají hned tři problémy:
− Execution cost: Vyčíslovat pořád dokola nějaké predikáty je výpočetně náročné. Kdyby
se měly predikátové zámky napsat, znamenalo by to testovat při každém zamčení a
odemčení splnitelnost nějaké sady predikátů (predikátové zámky kolidují pokud jsou
jejich podmínky současně splnitelné, nikoliv splněné).
− Pessimism: Pokud mám zámky s predikáty P1 a P2 takové, že vzájemně kolidují, pořád
ještě může existovat nějaké integritní omezení C nad databází takové, že P1 & P2 & C
není splnitelné. Systém ale o takovém omezení nemusí vědět a přesto (pesimisticky)
zamyká.
− Source: Kde se vlastně vezmou správné predikáty.
62
Takže špatná idea, ale možná na dobré cestě. Ukazuje totiž, že nejde o to zamykat jednotlivé
záznamy, nad kterými se pracuje, ale o to zamykat nějakým způsobem definované skupiny
záznamů. Odtud je už jen krůček k dalším nápadům.
8.3 Zámky s různou granularitou
V databázovém systému se zjevně nabízejí některé úrovně zámků (tj. na úrovni databáze
souboru, záznamu, položky, ap.). Tyto zámky jsou v hierarchickém vztahu: zamknout záznam
znamená zamknout všechny jeho položky apod., tedy jsou v podstatě zvláštním případem
predikátových zámků. Hierarchii zamykání lze popsat pravidlem:
Zámek typu X (R, W) na úrovni N implikuje zámky typu X na všech úrovních větších než N.
Tím spolu ovšem mohou kolidovat zámky na různých úrovních a je třeba to při zamykání
testovat. To se ještě poměrně dobře dělá, pokud nově uzavíraný zámek koliduje s někým jemu
nadřazeným, ale už by se to špatně programovalo v opačném případě (zamčení by znamenalo
projít celý podstrom a zkontrolovat všechny existující zámky, jestli nekolidují). Proto se
zavádí intenční zámky (intention locks) (I koliduje s R i W) a pravidlo pro hierarchické
zamykání:
Abych mohl na úrovni N zamknout zámek typu X (I, R, W), musím nejprve na úrovni N-1
zamknout zámek typu I
Tato metoda umožňuje dobře testovat existenci konfliktních zámků v hierarchii, ale je ještě
málo flexibilní (protože dva R zámky nad sebou v hierarchii by kolidovaly). Takže se intenční
zámky rozdělí na intenční zámky pro zápis a intenční zámky pro čtení. Ještě se zavádí zámek
read lock with write intention, což je zámek pro případy, že dovolím v podstromu číst, ale
nedovolím intention write, protože se chystám v podstromu zamykat na write.
Aby se předešlo typickému deadlocku při operacích typu READ-CHANGE-WRITE, kdy si
někdo čte na vyšší úrovni (zamkne pro čtení tabulku), aby pak části změnil (měnil jednotlivé
záznamy – pokud by to udělaly dva procesy, mohlo by docházet k deadlocku, kdy oba mají
read lock a chtějí dostat write lock), definuje se update lock. Ten je něco mezi zámkem pro
read a zámkem pro write, totiž pokud je něco zamknuto pro read, dá se to zamknout pro
update (a až bude chtít aplikace změnit update na write, tak počká), ale pokud je něco
zamknuté pro update nebo write, už to pro update zamknout nejde. Výskyt deadlocků výše
uvedeného typu je snížen na úkor zvýšeného počtu kolizí. Pokud má někdo zamknut zámek
pro update, už nelze zamknout další zámek pro čtení. Pokud chce držitel zámku pro update
začít zapisovat, čeká, dokud nejsou odemknuty všechny zámky pro čtení, ale má zaručeno, že
další zámky pro čtení nejsou povoleny.
Vylučovací tabulka vypadá následovně:
žádný
IR
IW
R
RIW
U
W
IR
+
+
+
+
+
–
–
IW
+
+
+
–
–
–
–
63
R
+
+
–
+
–
–
–
RIW
+
+
–
–
–
–
–
U
+
–
–
+
–
–
–
W
+
–
–
–
–
–
–
Tabulka 4: Vylučovací tabulka pro jednotlivé druhy zámků
Kompletní pravidla jsou následující:
− Zamyká se od kořenu k listům hierarchie.
− Odemyká se od listů ke kořenu hierarchie.
− Abych mohl zamknout IR nebo R, musím zamknout rodiče alespoň jako IR (tedy IR, IW,
R, RIW, W).
− Abych mohl zamknout IW, RIW nebo W, musím zamknout rodiče alespoň jako IW (tedy
IW, RIW, W).
8.4 Zamykání rozsahu klíčů
Zamykání s různou granularitou nad položkami, záznamy, soubory, atd. je sice pěkné, ale
příliš odráží fyzickou strukturu dat. Pokusme se nyní o přesazení zamykání do logické
struktury dat – zamykání rozsahu klíčů (key range locking).
Připomeňme si problém fantomů, který chceme (mimo jiné) řešit:
− Pokud T chce číst X a dozví se, že X neexistuje, nikdo nesmí X vytvořit, dokud T
neskončí.
− Pokud T přečte X a read next najde Y, nikdo nesmí mezi X a Y nic vložit, dokud T
neskončí.
− Pokud T smaže Y, nikdo nesmí vědět, že Y neexistuje, dokud T neskončí (to se týká i read
next na X, nikdo tudíž nesmí vložit nové Y).
Tady jsme nenápadně zavedli dva pojmy: existenci záznamu a pořadí záznamů. Oba se řídí
nějakým unique ordered klíčem – a my nyní umožníme zamykání rozsahu klíčů.
Sice je možné udělat zamykání rozsahu klíčů na pevných intervalech, ale to je vcelku
nezajímavé – místo toho se zaměřme na previous key locking, tedy zamykání, které pro každý
prvek databáze X před Y umí udělat zámek na rozsah klíčů [X,Y) (analogicky se dá udělat i
next key locking). Pravidla pro read unique, read next, insert a delete pak jsou:
− Read unique: Pokud čtu unique X, požádám o read lock na range X (ten je v previous
key locking [X,Y)), to zabrání jiným transakcím v modifikaci X – pokud X neexistuje,
požádám o read lock na předchozí klíč (ten je [W,Y)), čímž zabráním do ukončení
transakce vložit nové X.
− Read next: Pokud čtu next Y po X, už mám read zámek [X,Y), což zabraňuje vkládání
mezi X a Y - takže už jen zamknu [Y,Z).
64
− Insert: Pokud vkládám X mezi W a Y, zamknu write [W,Y) a write [X,Y), z [W,Y) se
vložením stane [W,X).
− Delete: Pokud ruším X mezi W a Y, zamknu write [X,Y) a write [W,X), z toho se pak
smazáním stane write [W,Y).
Je dobré si všimnout, že rozsahy klíčů nejsou libovolné! Zamykání rozsahu klíčů v
předvedené podobě má jeden malý problém – pokud čekám na zámek, nevím jaký rozsah se
přesně vrátí (např. pokud vložím X mezi W a Y a někdo jiný udělá na Y read previous, bude
chtít zamknout [X,Y), ale když vkládání abortne, X přestane existovat). To už se ale dá nějak
řešit pomocí dalších pravidel.
8.5 DAG zámky
Zámky na rozsazích klíčů jsou zjevně hierarchické. Co jsme ale nezmínili, je fakt, že zámky
na rozsazích klíčů mohou být vystavěny nad několika hierarchiemi klíčů, a tedy pravidla pro
zamykání na různé granularitě v dosud prezentované podobě nejdou aplikovat (protože zámky
mohou mít více rodičů).
Vezměme příklad select * from employees where eyes = "blue" and hair = "red".
Pokud bychom měli jeden klíč, řekněme employee_number, zámek připadající v úvahu by byl
[-INF,+INF) (protože employee_number není ve vztahu ke kritériím dotazu). Pokud máme
ale k dispozici indexy eye_color a hair_color, můžeme zamykat inteligentněji: Položky
dotazu teď zamykají rozsahy [blue,blue+1) v eye_color indexu a [red,red+1) v hair_color
indexu. To ale zavádí složitější hierarchii zámků, protože každý záznam má dva rodiče
(položku v eye_color indexu a položku v hair_color indexu).
Takže je potřeba umět granulární zámky na složitější hierarchie než stromy, jmenovitě
orientované acyklické grafy (directed acyclic graphs – DAGs). Tam jsou pravidla pro
zamykání následující:
− Zamyká se od kořenů k listům hierarchie.
− Odemyká se od listů ke kořenům hierarchie.
− Abych mohl zamknout IR nebo R, musím zamknout alespoň jednoho rodiče alespoň jako
IR (tedy IR, IW, R, RIW, W).
− Abych mohl zamknout IW, RIW nebo W, musím zamknout všechny rodiče alespoň jako
IW (tedy IW, RIW, W).
A mění se také pravidla pro implicitní zámky v nižších úrovních (tedy ona se nemění, jen se
zobecňují mírně nečekaným směrem):
− Pokud mám všechny rodiče nějakého uzlu zamčené jako W, mám současně i ten uzel
zamčený jako W.
− Pokud mám alespoň jednoho rodiče nějakého uzlu zamčeného alespoň jako R, mám i ten
uzel zamčený jako R.
Úprava z předchozí sady pravidel je přímočará. Pokud tedy vyrobíme nekolidující explicitní
zámky, nebudou kolidovat ani implicitní zámky.
65
8.6 Heuristiky
8.6.1 Konverze zámků
Co se stane, když má transakce zámek pro čtení a chce ho mít pro zápis? Transakce nemůže
pustit zámek pro čtení a potom ho zamknout pro zápis – narušilo by to dvoufázový uzamykací
protokol. Lze zamknout zámek v obou módech? Ne, zamknout zámek lze jen v jednom módu.
Zámek může být zkonvertován do jiného módu tak, že je ve svazu na následujícím obrázku
vybráno supremum z těchto dvou módů.
W
U
RIW
R
IW
IR
Obrázek 11: Svaz konverze zámků
8.6.2 Eskalace zámků
Existuje SQL příkaz (není ve standardu, ale bývá v implementacích):
LOCK TABLE IN [SHARE | EXCLUSIVE ] MODE;
Pokud se ukáže, že jemné zamykání (záznamů ap.) není optimální, změní se na hrubší
zamykání. Například při projíždění celé tabulky by se zbytečně pořád zamykaly jednotlivé
záznamy a přitom by stačilo jediné zamknutí.
Pokud systém udělá chybu, může dojít k tomu, že se zamkne nebezpečně velké množství
zámků, čili se zbytečně zabírá paměť. Proto systém tento stav detekuje (escalation threshold)
a začne konvertovat jemné zámky na hrubší zámky. Když se např. najde hodně R zámků,
hledají se zámky IR s hodně potomky s R zámky, pak se IR zámek nahradí R zámkem a
původní R zámky se mohou zrušit. Podobně pro W a IW zámky. Tento problém se jmenuje
lock escalation.
Escalation threshold je obvykle nastaven na 1000 zámků. Různé přístupy jsou pro dávkové
transakce nebo interaktivní transakce. Dávková transakce se nejdříve rozjíždí pomalu, než
dosáhne hranice 1000 zámků, pak jsou zámky zkonvertovány na file-granularity.
66
Databáze Rdb (před léty koupená Oracle) má jiný přístup: Implicitní není record-granularity,
ale file-granularity. Když je potřeba, ten kdo drží zámek je požádán, aby provedl deescalation k jemnějším zámkům.
Ještě je potřeba si uvědomit, že dnešní systémy mají nejjemnější zámky obvykle na úrovni
záznamů (existují i zámky na úrovni položek). Záznam má přiměřeně malou velikost. Jiné to
je u objektově orientovaných systémů – objekt se skládá z mnoha záznamů, takže je potřeba
zamykat množiny záznamů, části objektů ap.
Zamykání na úrovni stránek je snadno implementovatelné, řeší výskyt fantomů, navíc některé
recovery systémy nepodporují změny provedené více transakcemi na jedné stránce. Problém
je, když všechny populární záznamy jsou zrovna na jedné stránce: pak je zamykání po
stránkách skoro to samé, jako kdybychom zamkli všechno. Některé další problémy:
− V různých adresářích a seznamech se dávají k sobě podobné věci na jednu stránku.
− Sekvenční ukládání do souboru: zamknutí stránky obsahuje vždy konec souboru, takže
jiná stránka nemůže nikdy vkládat
8.7 Zamykání v nested transakcích
Předchozí úvahy byly jen o flat transakcích. Jiné je to s nested transakcemi nebo transakcemi
se savepointy.
Sekvenční podtransakce jsou snadné: Všechny běží pod trid rodičovské transakce, BEGIN
WORK nebo COMMIT WORK přidá do zásobníku savepoint. Zámky se ukládají také do
zásobníku, tedy každý rollback odemkne zámky zamknuté v podtransakci, která provedla
ABORT.
Složitější je to s paralelními podtransakcemi. Podtransakce se navzájem nevidí. Každá
podtransakce může zdědit zámek, po skončení ho její rodič dostane zpět. Předpokládá se, že
během doby, kdy jsou podtransakce aktivní, rodičovská transakce zámky nepotřebuje. Při
abortu podtransakce je zámek odemknut, pokud byl zamknut touto podtransakcí. Každá
podtransakce navíc může mít vlastní zámky, které po jejím COMMITu získá její rodič, po
jejím ABORTu získá zpět jen své zámky.
Pokud transakce čeká na zámek, který drží jiná transakce, provede se tento test:
Je to transakce ze stejné rodiny?
Pokud ano, je to nějaký předek?
Pokud ano, zámek se zdědí
Pokud ne čekej, pak se zámek zdědí
Pokud ne, čekej
Je patrné, že test na členství v rodině musí být hodně rychlý. Proto trid obsahuje i jméno
rodiny.
67
Nested transakce s paralelismy jsou ve stádiu úvah a hledání zajímavých koncepcí.
8.8 Plánování a deadlock
Lock manager je vlastně také plánovač (plánování je důležité pro transakční systém). Když
čeká na odemknutí zámku hodně procesů, tradičně je lock manager vybírá podle FIFO
kritéria. Problémem je tzv. priority-inversion problem, kdy např. proces s nízkou prioritou
drží nějaký zámek a proces s vysokou prioritou čeká. Celé plánování tedy vyjde nazmar.
Řešení je preempce. Lock managery by tedy zřejmě měly spolupracovat s plánovači.
8.8.1 Konvoj
Název vzniknul v roce 1979. Jde o to, že při FIFO plánování se stává hodně používaný zámek
plánovačem. Častý výskyt konvojů (convoys) je u zámku logu.
Příklad: Přístup procesů P1, P2, P3, P4,… k logu zámku. P1 má nižší prioritu a přesto všichni
musí čekat na to, až odemkne zámek. Pořadí procesů na zámku P2, P3, P4 se nebude měnit
(mají stejnou prioritu). Například než zámek P3 stačí změnit stav wait na stav lock, bude už
P2 znovu na konci fronty čekající na zámek.
Problém se stěžuje tím, že odložení procesu (lock, wait, dispatch, unlock) zabere desetkrát
více instrukcí než prosté zamknutí a odemknutí. Zatížení procesoru může vzrůst a navíc
plánovač procesů neplní svou funkci.
Řešení (doporučení):
1. Nepoužívat na přetížených zámcích FIFO plánování, raději všechny probudit a nechat
plánovač rozhodnout, kdo zámek získá.
2. Raději na zámek aktivně čekat, než zbytečně odložit proces. Než se stačí proces odložit,
může už být zámek odemčený.
3. (Neumožnit preempci procesů, které drží přetížený zámek.)
8.8.2 Předcházení deadlocků versus jejich tolerance
Definice deadlocku je všeobecně známá:
Množina procesů je zablokována (v deadlocku), jestliže každý proces z této množiny čeká na
událost, kterou může způsobit pouze jiný proces z této množiny.
Typicky se jedná o přidělování nějakého prostředku, kdy procesy v kruhu čekají na uvolnění
prostředků jinými procesy, které ale rovněž čekají na uvolnění prostředku.
Stejně tak jsou známy čtyři nutné podmínky pro vznik deadlocku:
1. Vzájemné vyloučení (mutual exclusion condition): Každý prostředek je buď přidělen
právě jednomu procesu, nebo je volný.
68
2. Drž a čekej (hold and wait condition): Procesy aktuálně vlastnící nějaký prostředek
mohou žádat nové prostředky.
3. Neodnímatelnost (no preemption condition): Přidělené prostředky nemohou být vzaty
nazpátek hrubou silou (nejsou preemptivní). Musí být vráceny explicitně procesem, který
je vlastní.
4. Čekání do kruhu (circular wait condition): Existuje kruhový řetěz procesů, kde každý z
nich čeká na prostředek, který je držen dalším článkem řetězu.
Jsou-li splněny všechny tyto čtyři podmínky, může se vyskytnout deadlock. Jestliže jedna z
nich není splněna, deadlock nemůže vzniknout.
Všeobecně se dá říci, že existují čtyři základní strategie pro řešení zablokování:
1.
2.
3.
4.
Vůbec si problému nevšímat.
Detekce a zotavení.
Dynamické předcházení opatrným přidělováním prostředků.
Prevence zajištěná nesplněním jedné z nutných podmínek.
V tomto textu nebudeme probírat všechny možnosti boje proti deadlocku, raději se zaměříme
na deadlock z pohledu transakcí.
Deadlocku se dá předcházet pomocí triviálního transakčního řešení: Nikdy nečekat, místo
toho udělat rollback a zkusit to znovu. To ale může vést ke stejné situaci (livelock), navíc se
nedá detekovat.
1. Známá metoda předcházení deadlocku: Lineární očíslování zařízení, zařízení je možné
zamykat v tomto pořadí. Tím se vyloučí možnost vzniku cyklu.
2. Konvence. Každý proces musí dopředu oznámit, jaké bude mít požadavky na zařízení.
3. Čekání omezit timeoutem. Pokud je timeout překročen, prohlásí se, že je něco špatně, a
provede se rollback. Používá IBM (CICS), Tandem (Encompass) a další SQL systémy.
Poznámky:
− Všechny aplikace musí dodržovat pravidlo timeoutu – tedy i čekání na terminálový
vstup může znamenat rollback.
− Čekání na zámky musí být výjimečné, jinak bude systém pořád provádět rollback.
Předpokládá se, že většina zámků je pořád k dispozici. Pokud jsou čekání málo časté,
deadlock je málo častý na druhou.
− Nicméně, timeout je dosti pesimistický – každé delší čekání se prohlásí za deadlock.
Kompromis je po vypršení timeoutu nějaké transakce detekovat deadlock, vybrat oběť
a na ní provést rollback.
Třetí metoda je statická, čili pesimistická. Častější jsou dynamická řešení – řekne se, že k
deadlocku nedochází moc často, takže se dá řešit standardním rollback mechanismem.
8.8.3 Wait-for graf a detekce deadlocku
Definice wait-for grafu: Transakce jsou uzly a orientovaná hrana je od T k T' pokud:
− T čeká na zařízení držené T', nebo
69
− T bude čekat na zařízení, které bude vlastnit T' (tj. pokud jsou oba ve frontě čekajících na
zařízení, T je za T' a požadavky na zařízení nejsou kompatibilní).
Procesy v cyklu = procesy v deadlocku. Pokud je plánování FIFO, pak každý proces čeká
právě na jeden proces, počet hran vycházejících z uzlu je 1. Hledání deadlocků odpovídá
hledání cyklů, graf je typicky řídký a cykly jsou spíš krátké (délky 2 nebo 3).
Každý zámek má dva seznamy: seznam granted a waiting. Každý seznam má tvar
<<ti,mi>, … > kde ti je transakce a mi je mód zámku. Hrana TàT' je přidána do grafu, pokud
T je v seznamu čekatelů a T' je v seznamu čekatelů před T nebo v seznamu transakcí držících
zámek, přičemž módy m a m' musí být nekompatibilní. Co se týče konverzí zámků, když je T
v obou seznamech, prohlásí se, že nečeká sama na sebe. Pro každou transakci se vytvoří
seznam zámků, na které čeká. Z obou seznamů pro každý zámek sestrojíme wait-for graf.
Tento graf se pak prohledá do hloubky (složitost je počet hran – využije se vlastnosti, že z
každého uzlu vychází nejvýše jedna orientovaná hrana).
8.8.4 Distribuovaný deadlock
Každý uzel vlastní své zámky – wait-for graf je nutné vytvořit ze seznamů čekatelů a držitelů,
které jsou rozprostřeny po síti.
Centralizované řešení: Je vybrán uzel, kde probíhá detekce. Každá transakce má domovský
uzel, který musí vědět, kam všude je nutné se kvůli dané transakci podívat.
Lepší řešení: path pushing. V nějakém uzlu se zjistí, že v listu čekatelů je transakce z jiného
uzlu. Této transakci se pošle zpráva "tebe blokuji". V novém uzlu se činnost opakuje až třeba
dojde zpráva zpět k iniciátorovi. Takto se může objevit i více deadlocků najednou. Problém
je, když je více iniciátorů, to se ale dá vyřešit.
Problém je, když některý resource manager nespolupracuje na hledání deadlocků. Neexistuje
žádná ISO norma na wait-for graf. Existují systémy s periodickou detekcí deadlocku – jednou
se sestaví wait-for graf složený ze všech lokálních wait-for grafů a hledá se cyklus. Pozor na
phantom deadlocks! Význam je zřejmý, není možné dostat wait-for graf, který je konzistentní
v nějakém čase.
Pokud se už najde deadlock, je nutné vybrat nejmenší množinu transakcí:
− jejichž rollbackem se zruší všechny cykly ve wait-for grafu,
− jejichž rollback je nejlacinější.
Standardní je hladový algoritmus, který si vybere transakci s nejkratším rollback logem. To
musí být provedeno obezřetně – některé transakce mohou být důležitější, dražší ap.
Vezmeme-li v úvahu pravděpodobnost deadlocku (málo na druhou), je možná lepší, než
předpokládat, že všechny resource managery budou spolupracovat, udělat systém založený na
timeoutech.
70
8.8.5 Pravděpodobnost deadlocku
Zjednodušující příklad: Mějme n+1 procesů složených z r+1 akcí, každá akce je zamknutí
jednoho z R zámků, r+1-ní akce je odemknutí všech zámků. Čas všech operací je nějaká
jednotka (tedy zanedbáváme ho). Platí n*r << R.
Pravděpodobnost čekání nějakého procesu při pokusu o zamknutí je: V průměru každý proces
zamknul r/2 zámků, procesů je n, čili PW = nr/2R.
Pravděpodobnost, že proces T bude v některém z r kroků čekat je:
PW(T) = PW + (1 – PW) * PW + (1 – PW)^2 * PW + … + (1 – PW)^r–1 * PW =
= PW(1 + (1 – PW) + … + (1 – PW)^r–1) =
= PW(1 – (1 – PW)^r)/(1 – ( 1– PW)) =
(použije se a^n – b^n = (a – b) * (a^n–1 + a^n–2b + ... + ab^n–2 + b^n–1))
= 1 – (1–PW)^r =
(použije se rozvoj (1–PW)^r, důležitý zůstane jen člen (r nad 1) * PW)
≈ r * PW = n * r^2/2R
Jaká je pravděpodobnost deadlocku? Pravděpodobnost cyklu délky 2 je: pravděpodobnost
čekání nějaké T na T' (PW(T)) krát pravděpodobnost, že čeká T' (PW(T)) na T (děleno n), čili
PW(T)^2/n. Pro cyklus délky tři to je úměrné PW(T)^3 a delší cykly ani nemusíme počítat,
protože pravděpodobnost jejich výskytu je zanedbatelná (PW(T)^i).
Pravděpodobnost deadlocku transakce T je tedy blízká pravděpodobnosti výskytu cyklu délky
2, což je PW(T)^2/n = n*r^4/4R^2.
Pravděpodobnost výskytu deadlocku celkově je n^2*r^4/4R^2.
Pokud si představíme, že každá transakce probíhá vteřinu, dostaneme, jak často se v systému
vyskytne deadlock. Když se sníží kolizní oblast na polovinu, pravděpodobnost čekání klesne
4-krát a pravděpodobnost deadlocku klesne 16krát. Proto je důležité hrát si s granularitou
zámků a sdílenými zámky. Taky se nesmí přehánět počet konkurentních transakcí (pracujících
nad stejnými daty): pravděpodobnost výskytu deadlocku roste s jejich čtvercem.
8.9 Exotika
Myšlenka: žádné čekání = žádný deadlock. To samozřejmě není realizovatelné. Systémový
software musí zajistit, aby na nejnižší úrovni byly řídící struktury chráněny.
71
8.9.1 Field Calls
Tento nápad pochází z roku 1982. Je dobré co nejvíce zmenšit kolizní oblast – součin délky
zamknutí velikosti zamknutých dat. Častý příklad:
update inventory
set
quantity_on_hand = quantity_on_hand – :delta
where item = :item
Field calls jsou podobné predikátovým zámkům. Každý field call je akce na hotspot položce.
Akce se skládá ze dvou částí:
− predikát, např. quantity_on_hand > 100,
− transformace, např. quantity_on_hand = quantity_on_hand – :delta.
Někdy chybí predikátová část, někdy transformace. Postup je takový, že predikát je ihned při
požadavku testován a pokud není splněn je nahlášena chyba. Pokud je splněn, do REDO logu
se zapíše transformace, která ale bude provedena až při commitu. Takže během testování
predikátu bude jen na chvíli příslušný záznam zamknut pro čtení. Během commitu se v první
fázi znovu testuje predikát (zámek pro čtení na záznamu), pokud není splněn provede se
rollback, pokud je splněn zkonvertuje se v druhé fázi zámek na zápis, provede se
transformace a zámek je uvolněn.
Nevýhoda: Transakce nevidí změny, které provedla, protože ty se fakticky provedou až při
commitu. Takže mohou být nebezpečné násobné update operace. To se ovšem dá vyřešit
zákazem násobných update v jedné transakci.
Nikdy se neprovádí žádné UNDO! Transformace musí být komutativní!
8.9.2 Vylepšení Field Calls
Nápad z roku 1986: zamezit častým UNDO z 1. fáze commitu. U každého záznamu mít
dvojici [m,n], n se mění testováním predikátu a m provedením transformace. m udává
skutečnou hodnotu v záznamu, n hodnotu udává, kolik n bude, až se provede commit všech
transakcí, které už testovaly predikát a vložily záznam do REDO logu.
Pak mohou existovat následující tři typy čtení:
1. Normální čtení se zámkem pro čtení.
2. Čtení s predikátem: Vrací se TRUE, pokud je zaručeno splnění predikátu do commitu
nebo rollbacku (vylepšená field calls).
3. Dirty čtení (čtení poslední zapsané hodnoty).
IMS Fast Path má lazy commit: Pro všechny transakce existuje jediný log, takže je možné
uvolnit zámky už po skončení 1. fáze commitu. Po případném pádu by totiž v nejhorším
provedla update jiná transakce před vlastním update.
Problém sériového čísla: Je potřeba generovat jedinečná sériová čísla s těmito vlastnostmi:
72
1.
2.
3.
4.
5.
Čísla jsou monotónní, tj. když má transakce t commit po transakci t', pak seq(t) > seq(t').
Aplikace dostane sériové číslo před commitem.
Mezi sériovými čísly nejsou mezery.
Transakce mohou provést rollback.
Nesmí se dlouho čekat (stovky čísel za vteřinu).
Oracle řeší tento problém existencí mezer a porušením monotonie.
8.9.3 Optimistické a timestamp zamykání
Pesimistické metody jsou takové, kde se co nejdříve zamyká objekt, se kterým se bude
pracovat. Optimistické metody naopak zamykají objekt až při commitu.
Optimistické field calls (vznik 5 let po field calls): Žádné testování predikátu, ten se testuje
jen při commitu. Když se v commitu zjistí neplatnost predikátu, proveď abort.
Timestamp field calls: Při testování predikátu se přečte jeho časové razítko, při update se
testuje, zda je pořád stejné. Pokud ne, provede se abort. Pokud ano, proveď transformaci a pak
nastav nové časové razítko.
8.9.4 Time Domain Addressing
Nápad z roku 1973: Vždycky to fungovalo tak, že se na papír nebo nějakou tabuli napsala
nová hodnota, ale ta stará hodnota byla pořád k dispozici (vědělo se, že je stará). Chytré je
podvojné účetnictví, které pomáhá samo detekovat chyby. Původní počítačové systémy
fungovaly podobně – děrné štítky nebo pásky. Z jedné pásky se dávkově zpracovala nová.
Stará verze ale byla stále k dispozici.
Nové systémy umožňují měnit části souborů, jednotlivé záznamy, existují různé soubory
změn ap. Vše je podřízeno výkonu. Staré hodnoty jsou přepsány a jsou nenávratně pryč.
Z toho vychází návrh systémů, které nikdy nepřepisují původní data, místo toho budují
historii dat. Adresování objektů nebude jen jejich jménem, ale dvojicí <name, time>. V této
souvislosti se mluví o time domain addressing, version-oriented systems, a immutable objects.
Interbase, Oracle, Postgres, Rdb: historie objektu vypadá takto:
<<V0[t0, t1]>, <V1[t1, t2]>, <V2[t2, *]>>,
kde V0, V1, V2 je posloupnost hodnot a t0, t1, t2 je rostoucí posloupnost časů. V0 je platný
mezi časy t0 a t1, atd., současná hodnota je V2. Každá transakce má svůj čas, čtení hodnoty
objektu v čase t3 > t2 vrátí hodnotu V2 a posloupnost se změní na:
<<V0[t0, t1]>, <V1[t1, t2]>, <V2[t2, t3]>, <V2[t3, *]>>.
73
Pokud t1 < t3 < t2, je vrácena hodnota V1 a historie se nezmění. Pokud se zapíše do objektu
nová hodnota, musí být t3 > t2. Pokud není, transakce nemůže měnit hodnotu, protože běží v
minulosti, pokud je, historie se změní:
<<V0[t0, t1]>, <V1[t1, t2]>, <V2[t2, t3]>, <V3[t3, *]>>.
Commit způsobí zapsání nového záznamu do historie, abort jeho zrušení. Myslelo se, že toto
řešení bude univerzální a začne se používat, našly se ale neřešitelné problémy:
− Čte se obvykle poslední hodnota, což mění historii. Čtení se tedy najednou stane update
intenzívní. (Při čtení je nutné zapsat se do historie, aby nebylo možné provést zápis s nižší
časovou značkou).
− Místo aby se čekalo na uvolnění zámku, tady se rovnou prohlásí, že některé transakce běží
v minulosti, takže musí provést abort (čekání odpovídá abortu).
− Nic se neříká o granularitě.
− Není snadné udělat něco podobného jako field calls pro položky s častým update. Musela
by se pořád měnit historie.
− Není zřejmý vztah mezi pseudočasem a reálným časem.
Celá věc souvisí se (správou verzí) version management, ta ale není obsahem tohoto textu.
9. Log manager
Log manager se stará o to, aby každá operace provedená v rámci nějaké transakce byla
zaznamenána, a tedy aby bylo možné provést rollback, commit, nebo undo/redo v případě
restartu systému. Je tedy důležité, aby byly jeho funkce rychlé, protože každá operace
znamená jeden nebo několik zápisů do logu. Zápis taktéž musí umožňovat konkurenci.
− Představa: Log je SQL tabulka, sloupce jsou takové, aby bylo možné klást rozumné
dotazy (log squence number – LSN, RMID, TRID, time, prev_LSN, prev_TRID_LSN +
tělo logu, potenciálně hodně velké).
− Normální činnost: Log se zvětšuje, nikdo ho nečte, všichni jen zapisují (na konec). Pak
nastane potřeba číst (kvůli ABORTu) a nastává recovery fáze.
Proč je na to potřeba speciální manager a nestačí jedna obyčejná SQL tabulka? Musí se řešit
STARTUP systému, musí existovat protokoly pro bezpečný zápis a navíc se zajišťuje
správnost hlaviček logu (aplikace pošle hlavičku jen při přihlášení a pak už posílá jen vlastní
tělo zprávy, kterou chce zalogovat).
9.1 Logovací tabulky
Existují (pochopitelně) lokální a distribuované logy. Batch transakce mívají speciální dvou- či
více-dimenzionální logy, kde se do hlavního logu zapíše jen pointer na speciální log tabulku
pro danou batch transakci, aby nebyl hlavní log zbytečně dlouho off-line.
Ve skutečnosti to nebývají SQL tabulky, ale sekvenční soubory, ke kterým existují katalogy v
SQL tabulkách. Občas bývají duplikované, nebo jsou starší soubory uloženy jenom off-line.
74
9.2 Public interface k logu
Čtecí práva k logu by měla odpovídat čtecím právům k danému durable objektu. Proto musí
log manager komunikovat také se security managerem. Existuje speciální log authority pro
čtení a pro zápis. Vzhledem k tomu, že by komunikace se security managerem při každém
čtení a zápisu do logu byla časově drahá, je řešena jako session-oriented.
K logu existují dva různé interface pro čtení:
− Record-at-time: Přímý přístup k souboru, čtení jen hlavičky nebo části potenciálně
velkého záznamu.
− Set-oriented: Obdoba SQL dotazů.
Některé další zásady:
1. Neexistuje přímý přístup („pointer“) do logu.
2. Nelze inkrementálně zapisovat „velký“ záznam, např. kvůli tomu, že napřed mám část
REDO a pak UNDO. V takovém případě použiji více záznamů – mohl by totiž nastat
problém v případě pádu systému a jeho následného restartu.
3. Je třeba šetřit přesuny mezi adresovým prostorem klienta, adresovým prostorem log
manageru a vlastním logem (různé techniky).
9.3 Implementační detaily
Log není nikdy měněn, je tedy zamknut pro zapsání nového záznamu na konec. Při čtení není
potřeba zamykat. Kromě log managera existují obvykle ještě dva další démoni – flush daemon
a allocate daemon. Způsob jejich práce je zřejmý – první z nich se stará o zápis z bufferu
(pokud se zápisy cachují) na disk a druhý se stará o alokaci potřebných souborů na disku.
Opatrný zápis: Pokud se ukládá neúplná stránka, pak jednou dojde k jejímu přepisu novější
verzí. Co kdyby ale došlo k pádu systému uprostřed tohoto zápisu? Zjevně může být poslední
stránka logu v nekonzistentním stavu (někde mezi starou a novou verzí). Jsou dvě jednoduchá
řešení:
1. Sériový zápis: Zapíše se na dvě stránky za sebou. Takže nepovede-li se zapsat celou první
stránku, je k dispozici stará verze na druhé stránce a nepovede-li se zapsat na druhou
stránku, je k dispozici nová verze na první stránce. Když se stránka zaplní, pokračuje se s
další stránkou. Zjevně je dobré zapisovat rovnou plné stránky (cachování zápisů).
2. Ping pong: Střídá se zápis na i-tou a (i+1)-ní stránku, při recovery se přečtou poslední dvě
stránky a podle časového razítka se pozná, která je novější a tudíž platná.
Další řešení je použití speciálních driverů na disky WADS (Write ahead data set), které se
plní tak, že se zapíše do prvního volného sektoru a až se cylindr naplní, dá se všechno někam
do normálního logu.
75
10. Transakční manager
10.1.1
Interface
Transakční manager má dva základní interface: interface pro aplikaci a interface pro resource
manager.
Application Interface definuje obvykle následující funkce, které používají aplikace a které
mají jasnou sémantiku:
− begin_work, commit_work, abort_work (normální primitiva pro zahájení a ukončení
transakce),
− save_work, rollback_work, chain_work (speciální primitiva pro zahájení a ukončení
transakce),
− prepare_work (pro dvoufázový commit)
− read_context, get_transaction_status, my_trid (získávání stavových informací),
− leave_transaction, resume_transaction (pro suspendování a probuzení transakce).
Resource Manager Interface je interface, které vyžaduje (ve formě pointrů na callback
funkce) transakční manager po resource managerech zapojených do transakce:
−
−
−
−
−
prepare, commit, abort,
savepoint(LSN), undo_savepoint(LSN), redo_savepoint(LSN),
undo, redo,
TM_startup,
checkpoint.
10.2 Funkce transakčního managera
Transakční manager má (zejména) za úlohu:
1. Řízení commitu a rollbacku,
2. Media recovery,
3. Restart resource manageru a systémový restart.
10.3 Koncepty transakčního managera
10.3.1
DO-UNDO-REDO protokol
Každá operace (DO) provede něco s nějakým objektem a vytvoří log. Změněný objekt je
možné pomocí UNDO a logu vrátit do podoby před DO. Původní objekt je možné pomocí
REDO a logu znovu uvést do podoby po provedení DO.
void transaction_undo
{
declare cursor for transaction_log
76
select RMID, LSN
from LOG
where TRID =:trid
descaneding LSN;
int sqlcode;
open cursor transaction_log;
while (true)
{
fetch transaction_log into :rmid, :lsn;
if (0<>sqlcode) break;
rmid.undo(lsn)
}
close cursor transaction_log;
}
Obdobně to vypadá pro REDO log, jen se použije ascending a rmid.redo(lsn) (u UNDO je
zpracování LIFO, u REDO je FIFO).
Příklad: session recovery (jednosměrná komunikace – pipe).
operations: open, close, send, rcv
data: každý endpoint má seq_no vyslaných (přijatých) zpráv
zámky: žádné
send: po send následuje log(seq_no, data)
rcv: po rcv následuje log (seq_no, data) + SP
UNDO(open, close) = (close, open)
REDO(open, close) = (open, close)
UNDO(send) = cancel message
real operations: např. send na tiskárnu, odloží se do druhé fáze commitu.
10.3.2
Idempotence versus testování
Někdy může být problém provést REDO (např. ReSend). Dobrá vlastnost je idempotence
(restartability), neboli f(f(x)) = f(x). Např. v předchozím příkladu se posílá v send i seq_no,
77
takže tam by to fungovalo. Operace „posuň teleskop o 20°“ už by nefungovala. Operace
„posuň teleskop na souřadnice xy“ už je zase v pořádku.
Zástupná vlastnost je testovatelnost. Pak to může fungovat něco jako while (old_state) do
new_state.
10.3.3
Fyzický log
Záznam v logu v tomto případě říká, jaká byla původní hodnota a jaká je nová hodnota (plus
hlavička s LSN, trid, atd.). Tady záleží na granularitě, změní-li se jeden záznam v souboru,
nebude v logu nová hodnota celého souboru, ale jen hodnota záznamu. Jde vlastně o physical
logging, protože v logu popisuji konkrétní změny nějakých hodnot. Nevýhodou může být
neúměrná velikost logu a s tím spojená nízká efektivita – kvůli jednomu delete from where
musíme do logu napsat spoustu záznamů, nemluvě o změnách indexů, ap.
10.3.4
Logický log
Tento log je orientovaný na operace, takže záznam v něm popisuje provedenou operaci včetně
jejích parametrů. Jeden záznam takového logu tedy může „pokrýt“ spoustu záznamů ve
fyzickém logu (INSERT mění hodnoty v b-stromu, ve stránce, v záznamu, …). Předpokládá
se, že každá pod-akce je atomická (když by se nepovedl split b-stromu, byl by log k ničemu).
10.3.5
Fyzicko-logický log
Transakce se rozdělí na mini transakce, každá mini transakce se týká určité fyzické úrovně
(zařízení) a používá fyzické logy a exkluzívní zámky. Celá transakce používá logický log.
10.3.6
Jednobitový resource manager
Zavedeme si one-bit resource manager, na kterém se občas dají vysvětlit jednoduché příklady
z distribuovanými transakcemi. Tento resource manager udržuje pole bitů a dvě volání:
get_bit a give_bit. Po volání get_bit je daný bit zamknut, odemknut je až po commitu
nebo rollbacku dané transakce.
Ačkoliv tento resource manager vypadá primitivně, má řadu aplikací, např. pro alokaci
volných stránek v paměti, alokaci bloků pro soubory, atd.
10.3.7
FIX, WAL, Force-log-at-commit
Seznamme se s dalšími zásadami práce s logem. Jedné z nich se říká FIX (fixing the page) –
čtení a zápisy na fyzické úrovni musí být vždy pokryty semaforem na úrovni stránky, dokud
není vše zapsáno v logu.
Další dobrá zásada je WAL (write ahead logging). Zásada říká jednoduchou skutečnost, totiž
že by se mělo do logu zapsat vždy dříve, než se provede zápis do persistentní paměti.
78
Force-log-at-commit znamená, že při commitu je log vždy uložen do persistentní paměti.
10.4 Dvoufázový commit protokol
Dostáváme se k důležitému mechanismu, který se používá ve světě distribuovaných transakcí
– ke dvoufázovému commit protokolu (two-phase commit protocol). Jde o to, že pokud se na
transakci podílí více resource managerů (každý má rozběhnuté lokální transakce, které jsou
součástí globální, distribuované transakce), není možné provést commit bez předchozího
hlasování, že s tím všichni zainteresovaní souhlasí. Pokud je tedy centrální transakční
manager vyzván ke commitu transakce, nejdříve všem resource managerům pošle zprávu
PREPARE. V této fázi proběhne hlasování, kdy každý resource manager řekne, jestli bude
commitovat nebo ne. Pokud se vyskytne alespoň jeden RM, který hlasuje pro zamítnutí
commitu, je ve druhé fázi všem RM zaslána zpráva ABORT. Pokud všichni RM v první fázi
souhlasí s commitem, je v druhé fázi všem zaslána centrálním TM zpráva COMMIT.
Někdy je transakce distribuovaná mezi více transakčních managerů. Pak existuje jeden,
kterému se říká root TM nebo transaction commit coordinator. Pak vlastně existuje celý
strom transakčních managerů, tedy určitá hierarchie, kdy na každé úrovni existuje jakýsi
koordinátor commitu. Fáze PREPARE se dělá nejdříve lokálně a pak globálně (v případě, že
neprojde ani lokálně, není nutné ji dělat globálně)
10.4.1
Persistence objektů a dvoufázový commit
Persistence může být explicitní nebo implicitní (kromě jiných rozdělení). Implicitní znamená,
že v data jsou persistentní automaticky, což v praxi vypadá tak, že se ukládají do persistentní
paměti v nějaký dobře definovaný okamžik. V transakčním prostředí je tímto okamžikem (jak
jinak) commit transakce. Přesněji to vypadá tak, že při commitu se postupně procházejí
persistentní objekty a uloží se do příslušné persistentní paměti (databáze). Protože mohou být
persistentní objekty uloženy v různých databázích, je potřeba použít dvoufázový commit
protokol.
Představme si ale následující případ: objekt A i objekt C jsou uloženy v databázi X, objekt B
je uložen v databázi Y. Při commitu se postupně probírají použité objekty, které by měly být
uloženy. Objekt A je z databáze X, kam je uložen a současně databáze X hlasuje ve fázi
PREPARE. Totéž je provedeno s objektem B a databází Y. Nyní má být uložen objekt C do
databáze X, která je ale po fázi PREPARE, takže do ní nemůže být nic uloženo a nemůže se
znovu posílat PREPARE kvůli objektu C. Je tedy potřeba nová fáze PREPREPARE, ve které
jsou všechny objekty uloženy a pak teprve proběhne dvoufázový commit protokol
(V CORBA OTS objekty musí implementovat speciální interface Synchronization, kde
metoda before odpovídá fázi PREPREPARE. Podobně to funguje u EJB.).
Jiné řešení je umožnit REPREPARE, to ale současné standardy X/Open XA (ani OTS)
neumožňují.
10.5 Checkpointing
trid ~ <time_of_birthaday, TM_id, seq_no>
79
Po restartu systému je potřeba pomocí REDO logu obnovit původní stav výpočtu, protože
některé změny jsou jen v logu a ne v persistentní paměti. Redo fáze restartu nesmí trvat příliš
dlouho, takže je nutné periodicky provádět checkpoint, neboli provést akce z redo logu a data
uložit do persistentní paměti. Např. když má log více než 10000 záznamů, provede se
checkpoint.
Low-water mark je LSN, odkud se musí provést REDO. Je tedy snaha, aby nebyl rozdíl mezi
LSN posledně provedené akce a low-water mark příliš velký. Pro UNDO je low-water mark
nejmenší „živé” LSN.
10.5.1
Sharp checkpointy
Při sharp checkpointu se ukládají všechna data, což může trvat relativně dlouhou dobu (u 1bit resource manageru např. 1000 stránek bitů zamknout, uložit, odemknout). Uložení je
provedeno synchronně, takže low-water mark je roven aktivnímu LSN. Ukládá se do dvou
kopií, kvůli chybě.
10.5.2
Fuzzy checkpointy
Fuzzy checkpoint se provádí asynchronně během normální činnosti systému, takže není
zajištěna konzistence ukládaných dat. Převod na sharp chceckpoint se provede pomocí logu
akcí provedených během provádění fuzzy checkpointu (1 bit RM – nečeká se na uložení 1000
stránek, checkpoint je nekonzistentní, ale každá sránka je konzistentní). Low-water mark je
roven LSN z okamžiku začátku provádění fuzzy checkpointu.
Je vidět, že je nutná idempotence operací, použije se log ze začátku provádění checkpointu a
některé akce se tudíž provedou dvakrát (během provádění checkpointu se už provedly a neví
se, kdy přesně to bylo).
Optimalizace se provádí dvojím způsobem: Jednak je možné si označovat změněné stránky a
při checkpointu ukládat jenom ty. Další zrychlení se dosáhne vytvářením kopie stránky v
paměti, aby stránka nemusela být během I/O zamknuta.
10.6 Restart
Transakční manager zajišťuje restart v případě pádu systému. Rozlišujeme hotstart, kdy se
restartuje za chodu systému, warmstart, kde se startuje za použití persistentní paměti, a
coldstart, kdy se startuje z nějakého archivu. Tato kapitola pojednává o warmstartu.
Existují následující důvody pro restart:
1. Pád systému: V případě pádu systému je ztracen obsah paměti, ale je zachován obsah
persistentní paměti. Aktivní transakce by měly být z pomocí logu korektně abortovány.
Mělo by být zajištěno, že výsledky commitovaných transakcí jsou persistentní.
2. Poškození media: Může se třeba stát, že se poškodí disk. Je nutné duplikovat (aspoň
občas) persistentní paměť.
80
3. Abort transakcí: Transakce mohou abortovat a v některých případech jsou poté znovu
restartovány (třeba v případě detekce deadlocku).
V této kapitole předpokládáme, že jsou všechny operace vratné pomocí svých UNDO operací.
Jak se zachází s nevratnými operacemi (real operations) už jsme se zmínili dříve. Dále se
budeme zabývat pouze transakcemi, které provádějí operace přímo a neodkládají je až do fáze
COMMIT. V transakcích s odloženým commitem se v době COMMITu nejdříve musejí
provést pomocí REDO logu všechny operace, které byly provedeny v rámci transakce. Po
případném pádu systému v době zpracovávání REDO logu je možné díky idempotenci REDO
operací provést znovu REDO všech operací v REDO logu. UNDO se v takovém případě
neprovádí.
Stavy transakce vzhledem ke dvoufázovému commitu jsou:
− Completed: Po druhé fázi 2pc protokolu, každý RM je už o commitu informován.
− Completing: Po první fázi 2pc protokolu, některé RM ještě nedostali potvrzení o finálním
commitu (tedy uprostřed druhé fáze).
− Persistent: V první fázi 2pc nebo v persistentním savepointu (nelze provést commit ani
abort).
− Active: Uprostřed práce (pak dělá i UNDO).
Stavy transakce podle toho, kdy začaly a kdy skončily, jsou:
− Transakce začala i skončila před posledním checkpointem. Taková nás zjevně nezajímá.
− Transakce začala před posledním checkpointem, ale skončila dříve, než nastal pád
systému. Zde je potřeba provést znovu operace mezi checkpointem a commitem.
− Transakce začala před posledním checkpointem a byla aktivní v době pádu systému. Je
potřeba provést UNDO.
− Transakce začala po posledním checkpointu, ale skončila dříve, než nastal pád systému.
Zde je potřeba provést znovu operace mezi checkpointem a commitem.
− Transakce začala po posledním checkpointu a byla aktivní v době pádu systému. Je
potřeba provést UNDO.
Ještě se na to můžeme podívat tak, že transakce může být commitována, ale uložen je pouze
log, zatímco vlastní efekt transakce ještě není externalizován do persistentní paměti.
Restart by se měl provést po ohlášení každého zainteresovaného RM, ve skutečnosti se čte
celý log a REDO i UNDO se dělá najednou na všech RM (ušetří se čtení logu). Stavy
transakce podle fáze, ve kterých jsou potvrzení první a druhé fáze u jednotlivých RM, se musí
řešit individuálně pro jednotlivé RM. V následujícím algoritmu se tedy transakcemi myslí
jednotlivé lokální transakce jednotlivých RM:
Přesný algoritmus restartu:
81
− Recovery manager má dva seznamy: UNDO list, který iniciálně obsahuje všechny aktivní
transakce v době posledního checkpointu (jejich seznam je uložen v logu), a REDO list,
který je iniciálně prázdný.
− Prohledává se od low-water mark po konec logu. Pokud se najde BEGIN WORK nějaké
transakce, přidá se tato transakce do UNDO seznamu. Když se najde COMMIT WORK
nějaké transakce z UNDO seznamu, tak se tato transakce přesune z UNDO seznamu do
REDO seznamu. Tímto prvním "REDO" čtením se dostane transakční manager do stavu,
ve kterém se nacházel v okamžiku pádu systému.
− Pak se prohledává zpátky od konce logu po low-water mark a provádí se UNDO transakcí
z UNDO seznamu.
− Nakonec se prochází znovu od low-water mark po konec logu a provede se REDO všech
transakcí z REDO seznamu.
Může se stát, že nespadne celý transakční systém, ale jenom některý RM. Pak se použije
stejný postup, ale použijí se jen záznamy transakcí, které pracovaly s daným RM.
Jak to, že restart vždy funguje správně? Viz následující tabulku:
Případ
Stav
transakce
Log
záznam
Persistentní
stránka (data)
Proč funguje
1
committed
volatile
stará
nemožné díky force-log-at-commit
nová
nemožné díky WAL a force-log-atcommit
stará
REDO vyrobí nová data
nová
REDO nevadí díky idempotenci
stará
žádný záznam při restartu (implicitní
UNDO)
nová
nemožné díky WAL
stará
REDO a pak UNDO
nová
REDO idempotence a pak UNDO
2
3
durable
4
5
aborted
volatile
6
7
8
durable
Tabulka 5: Proč funguje restart
Poznamenejme, že díky tomu, že je nejdříve vždy provedeno REDO a pak teprve UNDO,
nemusí být UNDO log idempotentní. Ve čtvrtém případě nás možná zarazí, že se provádí
REDO. Je to proto, že některé efekty commitované transakce nemusejí být v okamžiku
commitu v persistentní paměti, takže se provádějí znova. Kromě toho, recovery manager
může tímto krokem obnovit svou vnitřní strukturu. V případě 7 se před UNDO provádí ještě
REDO, zatímco v dříve uvedeném algoritmu se REDO před UNDO neprovádí. V algoritmu
se ale předpokládalo, že před fází UNDO (nebo i REDO) je provedeno REDO transakčního
managera (při dopředném čtení se vytvářejí REDO a UNDO seznamy), tady znovu
předpokládáme fázi před UNDO ještě fázi REDO transakčního managera.
82
10.7 Další vlastnosti transakčního managera
Nyní se seznámíme s některými rozumnými rozšířeními transakčního managera.
10.7.1
Heterogenní commit koordinátoři
Rozsáhlé sítě vyžadují komunikaci mezi spoustou různých transakčních managerů a resource
managerů. De facto standardem je IBM LU 6.2, který pochází z nejpopulárnějšího
transakčního monitoru CICS. De jure standardem je OSI-TP standard od X/Open.
Rozlišujeme open commit protokol a close commit protocol. Ten první pracuje tak, že RM se
podílí na commit protokolu a formáty dat jsou veřejné. Ten druhý je takový, že se formáty dat
neznají. Jde o historickou záležitost (IBM IMS, Tandem TMF). Ještě větší problém nastává,
používají-li se queued transakce. Akce se na takovém TM odkládají až do první fáze 2pc.
10.7.2
Dobře dostupní (neblokující) koordinátoři commitu
Doteď jsme se bavili o spolehlivosti, což takhle dostupnost (high availability)? Process pair –
non-blocking by single failure, tj. všechno se pojistí zdvojením (zejména HW).
Co ale udělat, když koordinátor stejně spadne? Při distribuovaném 2pc je možné v první fázi
poslat seznam všech zainteresovaných a v případě pádu koordinátora nečekat a po domluvě s
ostatními ze seznamu provést commit (aby se nemuselo zbytečně čekat v blokovacích
stavech).
Pro vyřešení problému pádu commit koordinátora byl navržen třífázový commit protokol.
Dvoufázový protokol totiž neumožňuje individuální zotavení všech zainteresovaných; místo
toho se čeká na restart koordinátora. Ve 3pc je (jak název napovídá) více fází, zavádějí se
timeouty a eventuální volba nového commit koordinátora.
10.7.3
Heuristická rozhodování během commitu
2pc se může provést tak, že se nečeká na potvrzení souhlasu s commitem od koordinátora
(tedy od všech RM) a heuristicky se lokálně commituje (či abortuje) už dříve. Heuristické
chování může být always commit, always abort, always commit deposit transaction, always
commit transactions less than $1000, atd.
Přibývají tak nové persistentní stavy heuristically commited a heuristically aborted. Po
dokončení druhé fáze 2pc se porovnají výsledky a případné neshody řeší lidský zásah.
Obdobným rozšířením je zavedení operator commit. Význam je zřejmý, zkrátka je
operátorovi umožněno ukončit commitem nebo abortem nějakou transakci, která drží
zbytečně moc resourců. Výsledky prý nejsou lepší než při heuristickém commitu.
83
10.7.4
Přenos commitu
Kdo začíná transakci, je pak její koordinátor, někdy ale může být lepší předat tuto roli jinému
TM kvůli jeho vyšší spolehlivosti nebo kvůli jeho vyššímu výkonu. Pošle se zpráva YOU
ARE ROOT, pak se změní polarita 2pc protokolu. O tomto rozšíření se uvažuje pro OSI-TP
standard (1991!). Je třeba vyřešit situace, když by se role koordinátora předala více serverům,
nebo že zainteresovaní budou nadále kontaktovat původního koordinátora (to se dá řešit tak,
že se to při PREPARE všichni dozví).
10.7.5
Optimalizace dvoufázového commitu
Co optimalizovat:
− Delay: Čas mezi zahájením commitu a jeho dokončením.
− Message cost: Počet zasílaných zpráv.
− Write cost: Počet zápisů do logu.
Všechny tyto tři oblasti spolu samozřejmě úzce souvisí. Představme si systém, kde je N
transakčních managerů a L logů. Pak se zasílá při commitu 4*(N-1) zpráv (prepare, prepared,
commit, commited) a 2L zápisů do logu (commit, completed).
Jedna optimalizace je posílání zpráv ve stromu výšky H. Pokud je W zpoždění při zápisu do
logu a M je zpoždění při poslání zprávy, pak je celkový čas už jen H*(2*M+2*W).
Read-only commit optimization: Některé transakce nemají co dělat při 2pc, takže se
deklarují jako read-only. Ušetří se všechny nebo třeba jen polovina všech zpráv pro první
nebo i druhou fázi. Je to velice triviální nápad, ale ukazuje se, že se tím dá ušetřit ze všech
optimalizací nejvíce.
Linear Commit optimization: Seřazení TM do řady (případně do kruhu), pošle se
PREPARE a pak COMMIT. Počet zpráv je 2N (místo 4*(N-1)), ale zase to déle trvá.
11. Standardy, specifikace a implementace
11.1 OSI-TP, OSI-CCR, X/Open DTP
ISO (International Standard Organization) navrhla dva standardy týkající se transakcí a sice
OSI-TP (Open System Interconnection – Distributed Transaction Processing) a OSI-CCR
(Open System Interconnection – Commit, Concurrency Control, and Recovery). Tyto
protokoly jsou postaveny nad klasickým vrstevnatým ISO-OSI modelem síťové komunikace a
definují protokoly a formáty zpráv pro dvoufázový commit protokol.
ISO-OSI nenavrhují žádné programovací interface – ty navrhlo sdružení výrobců X/Open
DTP. Zavádějí se pojmy jako transaction manager, resource manager, atd. Transakce v pojetí
X/Open DTP vypadají zhruba takto:
− existuje 2pc protokol,
84
−
−
−
−
−
−
−
root dělá vždy commit (žádný transfer),
read-only optimalizace,
heuristické transakce,
podtransakce,
zřetězené transakce,
timeouty pro transakce,
commit protokol je otevřený – spolupracuje s jinými TM.
Základní model má čtyři hlavní typy komponent:
1.
2.
3.
4.
Applikační programy
Resource Managery
Transaction Managery
Communication Resource Manager pro komunikaci mezi různými transaction
managery.
Standard definuje následující interface:
1. TX Interface: Interface mezi aplikačním programem a transaction managerem, které je
implementováno transaction managerem. Tento interface definuje klasické operace pro
zahájení a ukončení transakce, atd:
Funkce
Popis
tx_open
Opens the transaction manager and the set of associated
resource managers.
tx_close
Closes the transaction manager and the set of associated
resource managers.
tx_begin
Begins a new transaction.
tx_rollback
Rolls back the transaction.
tx_commit
Commits the transaction.
tx_set_commit_return
Commits the transaction.
Switches between chained and unchained mode. In the
case of chained transactions, the work is broken into
tx_set_transaction_control
pieces with each piece being under control of a flat
transaction. Once a piece of work is complete it is
committed or rolled back independent of the state of the
85
other pieces.
tx_set_transaction_timeout
Sets a transaction timeout interval.
tx_info
Returns transaction information such as its identifier,
state of the transaction etc.
Tabulka 6: TX interface
2. XA Interface: Obousměrný interface mezi resource managery a transaction managery.
Definuje dvě množiny funkcí. První množina, xa_*() funkce, je implementována
resource managery pro použití transakčním managerem.
Funkce
Popis
xa_start
Directs a resource manager to associate the subsequent requests by
application programs to a transaction identified by the supplied identifier.
xa_end
Ends the association of a resource manager with the transaction.
xa_prepare
Prepares the resource manager for the commit operation. Issued by the
transaction manager in the first phase of the two-phase commit operation.
xa_commit
Commits the transactional operations. Issued by the transaction manager in
the second phase of the two-phase commit operation.
xa_recover
Retrieves a list of prepared and heuristically committed or heuristically
rolled back transactions
xa_forget
Forgets the heuristic transaction associated with the given transaction
identifier
Tabulka 7: XA Interface
Druhá množina funkcí, ax_*() funkce, je implementována transakčním managerem a
využívají ji resource managery:
Funkce
Popis
ax_reg
Dynamically enlists with the transaction manager.
ax_unreg
Dynamically delists from the transaction manager.
86
Tabulka 8: AX Interface of X/Open DTP Model for resource managers
3. XA+ Interface: Tento interface podporuje globální transakce mezi více transakčními
managery spojenými přes communication resource managery.
4. TXRPC Interface: Intreface pro komunikaci mezi aplikačními programy uvnitř
transakce.
5. CRM-OSI TP: Interface mezi communication resource managerem a OSI službami.
X/Open DTP model je standardem, který podporuje většina databázových systémů a
transakčních systémů.
11.2 Transakce v jazyce Java
11.2.1
TJava
TJava je navržena v článku pánů Alex Garthwaite a Scott Nettles z Computer and Information
Science Department na University of Pennsylvania [28]. Jde o rozšíření jazyka Java, které je
postaveno na dvou základních bodech:
− Rozšíření Java heapu tak, aby jeho části byly persistentní.
− Logování změn v heap tak, aby bylo možné provádět commit nebo rollback změn v rámci
transakce.
Programovací model je takový, že je rozšířena syntaxe jazyka Java. Klíčové slovo
transaction definuje novou transakci, která může být commitována nebo abortována
pomocí nově zavedených klíčových slov commit a abort. Není možné transakce vnořovat,
ale tvrdí, že by to neměl být problém. Když transakce abortuje, všechny hodnoty objektů
modifikovaných touto transakcí jsou vráceny zpět na hodnoty před začátkem transakce.
Pokud transakce commituje, jsou hodnoty objektů uloženy do persistentní paměti. Co se týká
zamykání, detekce deadlocků je ponechána na uživatelích.
Kterýkoliv objekt je možné označit jako persistentní (general-purpose persistence) a objekty
dostupné přes odkazy z persistentního kořene jsou rovněž persistentní (persistence by
reachability). Je zavedena nová třída Persistence s metodami mark, unmark, lookup a
restarted. V tomto modelu je celkem přirozeně commit zápisem do persistentní paměti a
abort čtením z persistentní paměti.
Implementace je postavena nad Sun JDK 1.0, modifikovanými komponentami jsou Java VM,
Java heap a Recoverable Virtual Memory (RVM).
Jako příští cíl si autoři kladli vytvořit podporu pro nested transakce, od doby vydání článku
ale s ničím novým nepřisli.
87
11.2.2
PJama
PJama (původně PJava, Persistent Java) byla navržena v roce 1996 na Univeristě v Glasgow,
Skotsko (M.R.Atkinson, M.J.Jordan, L.Daynes a S. Spence) [29] [30] [31]. PJama zavádí do
jazyka Java persistenci založenou na třech principech:
− Orthogonality: Data kteréhokoliv typu mohou být persistentní.
− Persistence Independence: Kód je stejný pokud pracujeme s normálními nebo
persistentními objekty. PJama přijímá i normální Java kód.
− Persistence Identification: Existuje mechanismus pro označení objektů jako
persistentních. Identifikují se persistence roots a veškerá data z nich dostupná jsou
persistentní (persistence by reachability).
Aplikace jsou v PJama asociovány s třídou PJavaStore, která reprezentuje persistentní
paměť a má obvyklé metody jako store, restore, store_all, atd. Kód podporující
persistenci je získán pomocí pre-procesoru a post-procesoru. PJama navíc nabízí transakční
API skrze třídu TransactionShell. Specializací této třídy mohou vznikat nové transakční
modely. Poznamenejme, že dosud nebyla implementována podpora transakcí, není vůbec
možno provést rollback a persistence je zajištěna jen díky periodickým checkpointům.
V Java VM byly změněny následující části:
−
−
−
−
−
−
persistent heap,
Object Cache,
automatický object faulting,
garbage collecting (persistence by reachability),
inkrementální algoritmy načítání stromu persistentních objektů,
zajištění transakčního chování.
Na podporu transakcí byly kladeny následující požadavky:
− Bezpečnost: Uživatelem definované transakční modely nesmí ohrozit bezpečnost jazyka
Java.
− Žádné změny do specifikace jazyka Java (jako je tomu v případě TJava).
− Nezávislost transakcí: Kód se nesmí lišit podle toho, zdali je použit v transakci nebo
mimo ni.
Základní interface společný pro všechny transakční modely se skládá z funkcí pro ohraničení
transakce, jako jsou begin, commit nebo abort. Advanced transakční modely tento interface
rozšiřují o nové operace, jako třeba split, join, operace pro označení transakce jako člena
nějaké kooperující skupiny transakcí ap. Sémantika operací se pochopitelně může pro
jednotlivé transakce lišit (srovnej commit u flat a nested transakcí). Systém, kdy transakční
modely jsou implementovány jako specializace třídy TransactionShell a kdy mohou být
některé metody přetíženy nebo přepsány, zjevně splňuje vytýčené požadavky. Různá
sémantika operací uvnitř a vně transakcí se zajistí pomocí post-procesoru, který zpracuje
přeložený kód, který tedy bude před zpracováním vždy stejný. Po zpracování post-procesorem
přibudou do kódu věci jako logování, zamykání, atd.
88
Autoři předpokládají, že jazyk PJama budou používat dva typy programátorů. Experti znalí
transakční teorie budou navrhovat nové transakční modely a běžní programátoři budou
programovat aplikace pracující s transakcemi.
Programování za použití transakcí vypadá tak, že se vytvoří instance potomka třídy
Další použití záleží na programovacím stylu:
TransactionShell.
− V procedurálním programovacím stylu je transakční instance asociována s objektem
implementující interface Runable. Zavolání metody Start pak spustí danou transakci.
− V událostmi řízeném programování je tělo transakce tvořeno všemi objekty Runnable,
které jsou do transakce zahrnuty pomocí metod enter a leave (poznamenejme, že počet
threadů v transakci není nijak omezen). Když všechny thready skončí, je transakce
skončena (pokud byla zavolána metoda TransactionSell.end(), tak se čeká na všechny
thready).
Řekněme si nějaké bližší implementační detaily jazyka PJama. Na konci úspěšně
dokončeného programu se vždy implicitně zavolá metoda StabilizeAll. Drobné problémy
jsou se statickými proměnnými – jestli se mají při eventuálním restartu inicializovat jednou
nebo pro každou transakci zvlášť. Je možné nastavit sadu callbacků pro každou statickou
proměnnou, takže pak se tyto callbacky volají v době restore, restart nebo retry.
K následujícím událostem je potřeba v potomcích třídy TransactionShell napsat handlery:
−
−
−
−
−
−
notifyBegin,
notifyEnd,
notifyAbort,
notifyInvokee,
notifyEndInvokee,
notifyFailedInvokee.
Pokud neexistuje kód přiřazený těmto událostem, použijí se ty implicitní. Pro práci s thready
ještě existují další události:
−
−
−
−
−
−
notifyThreadEnter,
notifyThreadLeave,
notifyFailedEnteredThread,
notifyStartThread,
notifyEndThread,
notifyFailedInnerThread.
Součástí kódu reagujícího na tyto události musí být zejména kód pracující s instancemi tříd
nebo LockingCapability. Instance třídy UpdateBookKeeper sleduje
objekty změněné všemi thready transakce. Pro jeden transakční shell existuje jeden
UpdateBookKeeper pro všechny thready. UpdateBookKeeper umí dělat Snapshot – použije
stav pro undo a redo operace. UpdateBookKeeper se může dotázat na všechny dostupné
Snapshoty. stabilize metoda ukládá změny do trvalé paměti (lze použít Snapshot pro
rollback, není-li během stabilize disablován). UpdateBookKeeper dále umí delegovat
UpdateBookKeeper
89
změněné objekty jinému UpdateBookKeeper objektu (lze parametrizovat objekty, jejíž
záznamy mají být delegovány). Pro nested transakce se používá tzv. global delegation –
všechny objekty jsou delegovány potomkem své rodičovské transakci.
je objekt, který umožňuje rollback změn v UpdateRecoveryKeeper objektu do
času, kdy byl Snapshot vytvořen. Má dvě metody: jedna pro restore stavu uloženého ve
Snapshotu a druhá potlačující Snapshot (nelze ho pak použít). AccessBookKeeper je
podobný jako UpdateBookKeeper, ale jde o seznam čtených objektů (použití pro delegaci
zámků ap.)
Snapshot
Lock manager manager je v PJama reprezentován třídou LockingCapability, která se
používá zejména k delegování zámků (podobné jako s update). Persistence a zamykání se
musí oddělit, protože některé modely nemají update a unlock ve stejný okamžik (chained
transakce). Automaticky se chrání všechny objekty, se kterými thread pracuje (s přihlédnutím
k detekci konfliktů pro danou LockingCapability), detekce konfliktů je specifická pro
jednotlivé modely (kompatibilní operace, závislosti). Zámky se buď všechny odmeknou, nebo
delegují (včetně závislostí). ConflictHandler je handler případných konfliktů, který může
být předefinován (definují se objekty implementující interface ConflictHandler). Jedním
z cílů autorů bylo i vytvořit DeadlockHandler, jehož funkce je zřejmá. Jak se s výše
zmíněnými třídami a jejich instancemi pracuje? Instance LockingCapability se vytvoří pro
každou instanci TransactionShell, která chce mít příslušný Lock Manager. Při vytváření
nových podtransakcí je budován graf závislostí mezi instancemi LockingCapability.
Nástin příkladu flat transakcí (příklady nepochází od autorů PJama, protože už dva roky
neexistuje implementace transakcí v PJama a nejsou ani žádné zprávy, že by se na systému
transakcí v PJama nějak dále pokračovalo):
class FlatTransaction extends TransactionShell {
LockCapability lc;
UpdateBookKeeper bk;
public void notifyBegin ( ) {
//
Create building blocks
bk = new UpdateBookKeeper( this );
lc = new LockCapability( this );
lc.boundThread( currentThread );
//
From now, the newly created LockingCapability will acquire the lock of
//
any object used by that thread
}
public void notifyEnd ( ) {
//
Store all updated objects
90
bk.stabilizeAll( );
//
Release all acquired locks
lc.unlockAll( );
}
public void notifyAbort ( ) {
//
Restore all updated objects
bk.useSnapshot( );
//
Release all acquired locks
lc.unlockAll( );
}
public void notifyThreadEnter( Thread newThread ) {
//
Bound thread to ezisting LockingCapability
lc->boundThread( newThread );
}
public void notifyFailedEnteredThread( ) {
//
Kill other threads and rollback them
//
(use method inherited from TransactionShell class)
kill( );
}
public void notifyInvokee( TransactionShell newTS ) {
//
Inner transaction inhibited
//
(use method inherited TransactionShell class)
inhibit( newTS );
}
}
Singlethreaded nested transakce:
class NestedTransaction extends TransactionShell {
TransactionShell parent = null;
91
LockCapability lc;
UpdateBookKeeper bk;
ColisionDetectGraph cdg;
public void notifyBegin ( ) {
//
Create building blocks
bk = new UpdateBookKeeper( this );
lc = new LockCapability( this );
lc.boundThread( currentThread );
transactionState = RUNNING;
//
From now, the newly created LockingCapability wil acquire the lock of
//
any object used by that thread
//
Nested: Add edge to colision detect graph
if ( null != parent ) {
cdg = parent.getCDG( );
cdg.addEdge( lc, parent.getLC( ) );
}
//
Top-level: Create new colision detect graph
else
cdg = new ColisionDetectGraph( lc );
//
From now, this nested transaction can create locks incompatible
//
with his parent
}
public void notifyEnd ( ) {
//
Top-level: Store updated objects and release all acquired locks
if ( null == parent ) {
bk.stabilizeAll( );
lc.unlockAll( );
}
//
Nested: Delegate all updated objects and acquired locks
else {
92
bk.delegateAll( parent.getUBK( ) );
lc.delegateAll( parent.getLC( ) );
//
Delete edge from colision detect graph
cdg.removeEdge( lc, parent.getLC( ) );
}
}
public void notifyAbort ( ) {
//
Restore all updated objects and release all acquired locks
bk.restoreSnapshot( );
lc.unlockAll( );
if ( null != parent )
//
Nested: Notify parent
parent.notifyFailedInvokee( this );
}
public void notifyThreadEnter( Thread newThread ) {
//
Inhibit new thread
//
(use TransactionShell class method)
inhibit( );
}
public void notifyInvokee( TransactionShell newTS ) {
//
Set parent link to new added child
newTS.setParent( this );
}
}
Nested transakce, kde jsou sourozenci konkurentní:
class ConcurrentNestedTransaction extends TransactionShell {
int transactionState = NOT_RUNNING; // Not used in this example
TransactionShell parent = null;
LockCapability lcHold;
LockCapability lcRetain
93
UpdateBookKeeper bk;
ColisionDetectGraph cdg;
public void notifyBegin ( ) {
//
Create building blocks
bk = new UpdateBookKeeper( this );
lcHold = new LockCapability( this );
lcRetain = new LockCapability( this );
lc.boundThread( currentThread );
//
From now, the newly created lcHold wil acquire the lock of
//
any object used by that thread
//
Nested: Add edge to colision detect graph
if ( null != parent ) {
cdg = parent.getCDG( );
cdg.addEdge( lcHold, lcRetain );
cdg.addEdge( lcRetain, parent.getRetainLC( ) );
//
From now, parent transaction retained locks can be acquired
//
by child transaction in incompatible mode,
//
holded locks cannot be acquired in incompatible mode
}
//
Top-level: Create new colision detect graph
else
cdg = new ColisionDetectGraph( lc );
}
public void notifyEnd ( ) {
//
Top-level: Store all updated objects and release all acquired locks
if ( null == parent ) {
bk.stabilizeAll( );
lcRetain.unlockAll( );
lcHold.unlockAll( );
}
//
Nested: Delegate all acquired locks
94
else {
lcRetain.delegateAll( parent.getRetainLC( ) );
lcHold.delegateAll( parent.getRetainLC( ) );
//
Delete edges from colision detect graph
cdg.removeEdge( lcHold, lcRetain );
cdg.removeEdge( lcRetain, parent.getRetainLC( ) );
}
}
public void notifyAbort ( ) {
//
Restore all objects and release all acquired locks
bk.restoreSnapshot( );
lcRetain.unlockAll( );
lcHold.unlockAll( );
//
Nested: Notify parent
if ( null != parent )
parent.notifyFailedInvokee( this );
}
public void notifyThreadEnter( Thread newThread ) {
//
Bound thread to existing LockingCapability
lcHold->boundThread( newThread );
}
public void notifyInvokee( TransactionShell newTS ) {
//
Set parent link to new added child
newTS.setParent( this );
}
}
V poslední době se autoři PJama zabývají class evolution, tedy zkoumají případy, kdy uložená
data neodpovídají binárně třídám, které se instancializují v aplikaci, protože došlo k jejich
změně. Definují se one-to-one výměny třídy v persistentním store, řeší se změny hierarchie
(change description language – replace, remove, rename), konverze instancí (conversion
classes).
95
11.2.3
JTS
je
Java Transaction API (JTA), Java Transaction Service (JTS)
mapováním
OTS
do
jazyka
Java.
JTS
obsahuje dva package:
org.omg.CosTransactions
a org.omg.CosTSPortability. Uživatelský interface
k transakcím definuje součást JTS – JTA. JTA je podobné XA a TX interfacům z X/Open
DTP. Opět se definují entity v distribuovaném transakčním systému, definuje se interface pro
zahájení a ukončení transakce, popíše se dvoufázový commit.
JTA obsahuje následující interface:
− javax.transaction.UserTransaction interface pro zahájení a ukončení klientské
transakce, zjištění stavu transakce ap.,
− TransactionManager interface, který slouží transakčnímu serveru zejména k získávání
transakcí, spouštění transakcí ap.,
− javax.transaction.Transaction interface, pomocí kterého pracuje transakční server
s transakcemi, umožňuje asociovat s transakcí nové resource, registrovat synchronizační
callbacky, startovat a ukončovat transakci ap.,
− javax.transaction.Synchronization s metodami afterCommit, beforeCompletion
a afterCompletion, které mají funkci podobnou PREPREPARE z kapitoly 10.4.1,
− javax.transaction.xa.XAResource interface, který reprezentuje klasický X/Open XA
interface, a díky kterému je možné JDBC připojení přes XA interface k databázím a
ukončení transakce podle dvoufázového commit protokolu,
− javax.transaction.xa.Xid interface, který reprezentuje identifikátor xid transakcí.
JTA interface se používá zejména v Enterprise JavaBeans.
11.2.4
Enterprise Java Beans
Enterprise Java Beans (EJB) is a technology specification from Sun Microsystems Inc. that
specifies a framework for building component-based distributed applications. Application
servers conforming to this technology are beginning to appear from various vendors over the
past six months, while the specification is being improved currently by Sun Microsystems Inc.
As an application server framework, the EJB servers address transaction processing, resource
pooling, security, threading, persistence, remote access, life cycle etc. However, as with the
case of MTS, this section focuses only on distributed transactional model of the EJB
framework.
The EJB framework specifies construction, deployment and invocation of components called
as enterprise beans. The EJB specification classifies enterprise beans into two categories:
entity beans and session beans. While entity beans abstract persistent domain data, session
beans provide for session specific application logic. Both types of beans are maintained by
EJB compliant servers in what are called as containers. A container provides the run time
environment for an enterprise bean.
96
Figure 5 shows a simplified architecture of transaction management in EJB compliant
application servers. This figure shows only the essential interactions between the constituent
parts of the architecture.
An enterprise bean is specified by two interfaces: the home interface and the remote interface.
The home interface specifies how a bean can created or found. With the help of this interface,
a client or another bean can obtain a reference to a bean residing in a container on an EJB
server. The remote interface specifies application specific methods that are relevant to entity
or session beans.
Clients obtain references to home interfaces of enterprise beans via the Java Naming and
Directory Interface (JNDI) mechanism. An EJB server should provide a JNDI implementation
for any naming and directory server. Using this reference to the home interface, a client can
obtain a reference to the remote interface. The client can then access methods specified in the
remote interface. The EJB specification specifies the Java Remote Method Invocation (RMI)
as the application level protocol for remote method invocation. However, an implementation
can use IIOP as the wire-level protocol.
In Figure 5, the client first obtains a reference to the home interface, and then a reference to
an instance of Bean A via the home interface. The same procedure is applicable for instance
of Bean A to obtain a reference and invoke methods on an instance of Bean B.
EJB Transaction Model
The EJB framework does not specify any specific transaction service (such as the JTS) or
protocol for transaction management. However, the specification requires that the
javax.transaction.UserTransaction interface of the JTS be exposed to enterprise beans.
This interface is required for programmatic transaction demarcation as discussed in the next
section.
Similar to the MTS, the EJB framework provides for declarative demarcation of transactions.
The container performs automatic demarcation depending on the transaction attributes
specified at the time of deploying an enterprise bean in a container.
The following attributes determine how transactions are created.
1. NotSupported: The container invokes the bean without a global transaction context.
2. Required: The container invokes the bean within a global transaction context. If the
invoking thread already has a transaction context associated, the container invokes the
bean in the same context. Otherwise, the container creates a new transaction and invokes
the bean within the transaction context.
3. Supports: The bean is transaction-ready. If the client invokes the bean within a
transaction, the bean is also invoked within the same transaction. Otherwise, the bean is
invoked without a transaction context.
4. RequiresNew: The container invokes the bean within a new transaction irrespective of
whether the client is associated with a transaction or not.
5. Mandatory: The container must invoke the bean within a transaction. The caller should
always start a transaction before invoking any method on the bean.
97
Transaction Demaraction
The EJB framework supports three types of transaction demarcation.
− Declarative Demarcation: This is also called as container managed demarcation. The
container demarcates transactions on behalf of the bean. The required attribute is specified
in a deployment descriptor at the time of deploying the bean on an EJB server. The bean
can use the javax.ejb.EJBContext.setRollbackOnly() method to mark the
transaction for rollback.
− Bean Managed Demarcation: This is similar to the client-managed demarcation.
− Client
Managed
Demarcation:
Java
clients
can
use
the
javax.transaction.UserTransaction
interface
to
demarcate
transactions
programmatically.
Resource Enlistment
Resource enlistment is automatic with EJB. The EJB containers automatically enlists
connections to EJB-aware resource managers whenever a bean obtains a connection.
Application Synchronization
The EJB specification provides the javax.ejb.SessionSynchronization interface for
application synchronization. When implemented by a bean, the container calls the
afterBegin, beforeCompletion and afterCompletion methods for application
synchronization during the two-phase commit process.
There are several vendors who currently offer application servers complying to the EJB 1.0
specification. Some of the products are WebLogic from BEA Systems Inc., GemStone/J from
GemStone Systems Inc., NetDynamics from Sun Microsystems Inc., Oracle Application
Server from Oracle Corporation, and PowerTier for EJB from Persistence Software Inc..
Refer to the EJB Directory for a listing of EJB application servers and EJB-aware database
servers. The specification is currently undergoing a major overhaul, and products complying
to the new standard are expected by late 1999 or early 2000.
In summary, the EJB framework provides features almost similar to the MTS. Both the
technologies allow component-based transactional distributed applications, and abstract the
process of transaction demarcation from application components.
11.3 Transakce v systému CORBA: Object Transaction Service
(OTS)
Object Transaction Service (OTS) je služba podporující distribuované transakce v systému
CORBA navrženému konsorciem firem OMG (Object Management Group). OTS definuje
sadu interface tak, aby bylo možno používat různé CORBA objekty v transakcích. OTS je
v zásadě postavena na X/Open DTP modelu s následujícími rozšířeními:
− OTS nahrazuje funkcionální XA a TX interface IDL interfacem.
98
− Různé objekty v OTS komunikují pomocí IIOP.
OTS samozřejmě umí spolupracovat s klasickým X/Open XA.
OTS obsahuje následující komponenty:
− Transaction Client: A program or object that invokes operations on transactional objects.
− Transactional Object: A CORBA object that encapsulates or refers to persistent data,
and whose behavior depends on whether or not its operations are invoked during a
transaction.
− Recoverable Object: A transactional object that directly maintains persistent data, and
participates in transaction protocols.
− Synchronization Object
− Subtransaction-Aware Resource
− Transactional Server: A collection of one or more transactional objects.
− Recoverable Server: A collection of objects, of which at least one of which is
recoverable.
− Resource Object: A resource object is an object in the transaction service that is
registered for participation in the two-phase commit and recovery protocol.
In addition to the usual transactional semantics, the CORBA OTS provides for the following:
− Nested Transactions: This allows an application to create a transaction that is embedded
in an existing transaction. In this model, multiple subtransactions can be embedded
recursively in a transaction. Subtransactions can be committed or rolled back without
committing or rolling back the parent transaction. However, the results of a commit
operation are contingent upon the commitment of all the transaction's ancestors. The main
advantage of this model is that transactional operations can be controlled at a finer
granularity. The application will have an opportunity to correct or compensate for failures
at the subtransaction level, without actually attempting to commit the complete parent
transaction.
− Application Synchronization: Using the OTS synchronization protocol, certain objects
can be registered with the transaction service for notification before the start of and the
completion of the two-phase commit process. This enables such application objects to
synchronize transient state and data stored in persistent storage.
The following are the principal interfaces in the CORBA OTS specification.
Interface
Responsibilities
− Transaction
demarcation (begin,
rollback_only, set_time_out)
Current
commit,
rollback,
− Status of the transaction (get_status)
− Name of the transaction (get_transaction_name)
− Transaction context (get_control)
99
TransactionFactory
Explicit transaction creation
Control
Explicit transaction context management
Terminator
Commit or rollback a transaction.
− Status of the transaction (get_status, get_parent_status,
get_top_level_status)
− Transaction
information
(is_same_transaction,
is_related_transaction,
is_ancestor_transaction,
is_descendant_transaction,
is_top_level_transaction,
hash_transaciton,
hash_top_level_transaction, get_transaction_name,
get_txcontext)
Coordinator
− Resource
enlistment
(register_resource,
register_subtrans_aware)
− Registration
of
synchronization
objects
(register_synchronization)
− Set the transaction for rollback (rollback_only)
− Create subtransactions (create_subtransaction)
RecoveryCoordinator Coordinate
recovery in case of failure (replay_completion)
Resource
Participation in two-phase commit and recovery protocol
(prepare, rollback, commit, commit_one_phase, forget)
Synchronization
Application synchronization before beginning and after
completion of two-phase commit (before_completion,
after_completion)
SubtransactionAware Commit or rollback a subtransaction (commit_subtransaction,
Resource
rollback_subtransaction called by the transaction service
TransactionalObject A
marker interface to be implemented by all transactional objects
Tabulka 9: CORBA OTS Interface
V OTS
je
možné
propagovat transakci implicitně (implementací interface
TransactionalObject) nebo explicitně rozšířením hlaviček metod o jeden parametr.
V posledních letech se objevilo mnoho implementací OTS: prakticky každý výrobce
implementace prostředí CORBA implementuje i OTS.
100
11.4 Transakce v COM/DCOM: Microsoft Transaction Server
Microsoft Transaction Server (MTS) je transakční server pro komponentový systém COM
firmy Microsoft. MTS definuje sadu interface pro tvorbu transakčních COM komponent, ale
také existuje MTS runtime prostředí, které umožňuje deployment transakčních komponent,
jejich mangement a monitoring. Samozřejmě se může více komponent podílet v jedné
transakci. Na rozdíl od předchozího, MTS je implementace a není postaven na otevřené
specifikaci.
MTS má následující části:
1. MTS Run-time: It is the environment where instances of MTS components execute and
be managed. The MTS run-time provides for deployment and management of MTS
components. It has the following features:
− Management of distributed transactions
− Automatic management of processes and threads
− Management of objects (creation, pooling and recycling)
− Distributed security service to control object creation and usage
2. MTS Explorer: This is a graphical user interface driven tool for deploying and managing
MTS components on the MTS run-time. The MTS Explorer can also be used to monitor
transactions with the Distributed Transaction Coordinator.
3. Distributed Transaction Coordinator (DTC): DTC is the transaction manager for MTS.
4. MTS API: The MTS API (in Microsoft Visual Basicâ, Microsoft Visual C++â and
Microsoft Visual J++â) provides certain interfaces and concrete classes for building
transactional components.
5. Resource Dispensers: An MTS resource dispenser manages nondurable shared data on
behalf of MTS applications. MTS provides two resource dispensers:
− ODBC Resource Dispenser: The ODBC resource dispenser is essentially a ODBC driver
manager with the following additional capabilities:
− Manage pools of connections to ODBC compliant databases, including reclamation
and reuse of connections)
− Enlist and delist database connections on MTS context objects.
− Shared Property Manager: The MTS shared property manager manages applicationswide process-specific properties (name-value pairs) and provides synchronized access this
data.
6. Resource Manager: For a resource manager to participate in MTS transactions, it must
support one of the following protocols:
− OLE Transactions: This is a COM based two-phase commit protocol used by resource
managers to participate in transactions coordinated by the DTC.
− X/Open DTP XA Protocol: With this protocol, the MTS requires a OLE Transactions to
XA mapper. This mapper is provided by the MTS SDK.
MTS Objects and Transaction Context
An MTS object is an instance of an MTS component (a component deployed on MTS, and
managed by MTS). For each MTS object, the MTS creates and maintains a context object
101
(ObjectContext) which provides execution context for an MTS object. The context object
also maintains information of transaction context. Resource dispensers and the DTC can
access this transaction context information for transaction demarcation, resource enlistment,
delistment, two-phase commit etc. Note that, in MTS, the transaction context information is
maintained with each MTS object, as opposed to a single transaction context object for all the
objects participating in a transaction.
Transaction Outcome
Each MTS object can participate in determining the outcome of a transaction by calling one of
the methods on the ObjectContext object:
− SetComplete: Informs the MTS that the object has successfully completed its work, and
its work can be committed.
− SetAbort: Informs the MTS that the object's work can not be committed.
− EnableCommit: The object's work is not necessarily done, but its transactional work can
be committed in the current form.
− DisableCommit: Informs the MTS the object's work can not be committed in the current
form.
Transaction Demarcation
MTS allows both programmatic and declarative demarcation of transactions. Declarative
demarcation is compulsory for all components deployed on the MTS. In addition, MTS clients
can also initiative and end transactions programmatically.
− Declarative Demarcation: Depending on an MTS component's transaction property,
MTS automatically begins a transaction on behalf of the application. The following
transaction properties (that can be set at the deployment time) are possible:
1. Requires Transaction: Instances of the component always execute within the context of
a transaction. It is expected that the invoking object is being executing in the context of a
transaction.
2. Requires New Transaction: Instances of the component must execute within their own
transactions, irrespective of whether or not the invoking object has started a transaction.
3. Supports Transaction: Instances of the component can execute within the scope of the
invoking object's transaction (if there is one). This implies that the component is
transaction-safe.
4. Does not Support Transaction: Instances of the component do not execute with in the
scope of any transaction. MTS does not associate the work done by such objects with any
transaction.
− Programmatic Demarcation: MTS clients can demarcate transactions programmatically
using the TransactionContext object. A client can begin a transaction by creating an
instance of the TransactionContext object, and end the transaction by calling Commit or
Abort methods of this object. All MTS objects created within these boundaries using the
TransactionContext object will execute under the same transaction context (except
when the component is set to require a new transaction, or does not support a transaction).
MTS implicitly maintains the association between the TransactionContext object and
the transaction.
102
Resource Enlistment
MTS does automatic resource enlistment. When a MTS object requests the resource dispenser
for a connection to a resource, the resource dispenser obtains the calling object's transaction
context, and registers the connection with it.
Although MTS is available for Microsoft Windows platforms only, MTS can interoperate
with resource managers complying to the XA protocol, and such resource managers operating
on non-Windows platforms can participate in transactions coordinated by the DTC.
12. Použitá literatura
[1] Jim Gray, Andreas Reuter, Transaction Procesing: Concepts and Techniques, Morgan
Kaufmann Publishers, 1993
[2] Haiyan Hasse, Hans-Jörg Scheck, Unified Theory For Classical and Advanced
Transaction Models, in Object Orientation with Parallelism and Persistence, BurkHard
Freitag, Cliff B. Jones, Christian Lengauer, Hans-Jörg Scheck (editors), Kluwer
Academic Publishers, 1996
[3] Ahmed K. Elmagarmid (editor), Database Transaction Models for Advanced
Applications, Morgan Kufmann Publishers, 1992
[4] Sushil Jajodia, Larry Kerschberg, Advanced Transaction Models And Architectures.
Kluwer Academic Publishers, 1997
[5] Hans-Jörg Scheck, G. Weikum, H. Ye: Towards A Unified Theory of Concurrency
Control and Recovery, Proc. ACM Principles of Database Systems, 1993
[6] C. T. Davies: Data Processing Spheres of Control, IBM Systems Journal (17)2, February
1978
[7] Panayotis Kypros Chrysanthis: ACTA, A Framework for Modeling and Reasoning about
Extended Transactions, PhD thesis, September 1991, directed by Krithivasan
Ramamritham.
[8] J. E. B. Moss: Nested Transactions: An Approach to Reliable Distributed Computing,
PhD thesis, April 1981.
[9] Object Management Group: Object Transaction Service. December, 1997.
[10] Zarras, A., Issarny, V.: A Framework for Systematic Synthesis of Transactional
Middleware. Proceedings of Middleware '98, September 1998.
[11] Biliris, A., Dar, S., Gehani, N., Jagadish, H.V., Ramamritham, K.: ASSET: A System for
Supporting Extended Transactions. Proceedings of ACM SIGMOD International
Coference on Management of Data, May 1994
[12] Mohan, C.: Advanced Transaction Models: Survey and Critique. ACM Sigmod,
International Conference on Management of Data, May 1994.
[13] Wachter, H., Reuter, A.: The ConTract Model. In Ahmed K. Elmagarmid: Database
Transaction Models for Advanced Applications, 1991.
[14] Georgakopoulos,D., Hornick, M. F., Manola, F., Brodie, M. L., Heiler, S., Nayeri,F.,
Hurwitz, B.: An Extended Transactional Environment for Workflows in Distributed
103
Object Computing. IEEE Bulletin of the Technical Committee on Data Engineering, June
1993.
[15] Bukhres, O., Elmagarmid, A., Kuhn, E.: Implementation of the Flex Transaction Model.
IEEE Bulletin of the Technical Committee on Data Engineering, June 1993.
[16] Lomet, D., editor: Special Issue on Workflow and Extended Transaction Systems. IEEE
Bulletin of the Technical Committee on Data Engineering, June 1993.
[17] Shrivastava,S. K., Wheater, S. M.: Implementing Fault-Tolerant Distributed Applications
Using Objects and Multi-Cloloured Actions. 10th International Conference on
Distributed Systems, Paris, May 1990.
[18] Jajodia, S., Kerchsberg, L.: Advanced Transaction Models And Architectures. Kluwer,
1997.
[19] Elmagarmid, A. K.: Database Transaction Models For Advanced Applications. Morgan
Kaufmann, 1992.
[20] Matena,V., Hapner, M.: Enterprise Java Beans Specification 1.0. JavaSoft, March 1998.
[21] Jennings, R.: Microsoft Transaction Server 2.0. Database workshop, Sams Publishing,
1997.
[22] Daynes, L., et al.: Customizable Concurrency Control for Persistent Java. Chapter Seven
in Advanced Transaction Models and Architectures, Editors: S. Jajodia & L. Kerschberg,
August 1997.
[23] Daynes, L.: Extensible Transaction Management in PJava. First International Workshop
on Persistence and Java, September 1996.
[24] Wu, Z., Schwiderski, S.: Reflective Java: Making Java Even More Flexible, February
1997.
[25] Heineman, G. T., Kaiser, G. E.: The CORD approach to Extensible Concurrency
Control, IEEE International Conference on Data Enfineering, 1997.
[26] Sagas
[27] Benchmarks Handbook for Database and Transaction Processing Systems, J. Gray, 1991.
[28] Alex Garthwaite and Scott Nettles: TJava: A Transactional Java. IEEE International
Conference on Computer Languages, May 1998
[29] Malcom Atkinson, Laurent Daynes Mick Jordan, Tony Printezis and Susan Spence: An
Orthogonally Persistent Java. ACM SIGMOD Record, Volume 25, Number 4, December
1996
[30] Laurent Daynes: Extensible Transaction Management in PJava. First International
Workshop on Persistence and Java, September 1996
[31] L. Daynes et al.: Customizable Concurrency Control for Persistent Java. Chapter Seven
in Advanced Transaction Models and Architectures, Editors: S. Jajodia & L. Kerschberg,
August 1997
104

Podobné dokumenty

PowerUp® 3.0

PowerUp® 3.0 Nechtěné naklánění se vlevo nebo vpravo Řešení problémů Selhání nabíjení Přerušení spojení mezi chytrým modulem a aplikací před letem Ztráta komunikace během letu Úplná ztráta kontroly s př...

Více

Příklad

Příklad Ve starších verzích Oracle je nebylo možné upravovat, bylo možné se na ně jen dotazovat. Nemohou být indexovány, neboť pro řádky neexistují žádné hodnoty ROWID. Vhodné pro načítání a slučování dat ...

Více

Distribuované systémy

Distribuované systémy Lokální a vzdálené zdroje jsou přístupné s použitím identických operací. Dovoluje přístup ke zdrojům bez znalosti jejich umístění. Zdroj může být přesunut v systému na jiné místo, aniž by se tím ov...

Více

Medveduv pruvodce po LibUCW

Medveduv pruvodce po LibUCW Medvědův průvodce po LibUCW Martin Mareš [email protected] Katedra Aplikované Matematiky MFF UK Praha

Více

orsŠk levilhez

orsŠk levilhez Patka na našívaní skrytých zipů Pied de biche spécial pour fermeture à glissière invisible Rejtett húzózárvarró talp Picior special pentru coaserea fermoarelor ascunse Pätka na našívanie skrytých...

Více

BAKALÁŘSKÁ PRÁCE Dung NGUYEN TIEN Výuková aplikace

BAKALÁŘSKÁ PRÁCE Dung NGUYEN TIEN Výuková aplikace odemykají. Test je založen na faktu, že jestli jsou transakce dobře formované a zároveň dvoufázové, pak každý jejich legální rozvrh je serializovatelný. Jako poslední možnost je testování pomocí s...

Více

relační databáze - Ostravská univerzita

relační databáze - Ostravská univerzita jednoduchých relací. Již tento fakt naznačuje, že bude skutečně velmi složité a někdy dokonce nemožné realitu natolik zjednodušit. Tato úvaha vede ke snaze vývoje datových modelů založených na obje...

Více

RNDr. Jakub Lokoč, Ph.D.

RNDr. Jakub Lokoč, Ph.D.  Podíl přijatelné doby odezvy systému ku celkovému

Více

Herní design

Herní design  Budeme ale zkoumat vztah hry a příběhu

Více