:: Basi di Dati - Complementi - Lezione del 3 marzo 2008 ::
Appunti Glamour
Torna alla pagina di Basi di Dati - Complementi
Gestione delle transazioni
La transazione è un'unità elementare di lavoro che deve avere certe caratteristiche:
Un sistema transazionale è il meccanismo che definisce ed esegue transazioni da parte di più applicazioni concorrenti. Garantisce il corretto comportamento del sistema, secondo le proprietà qui sopra citate.
La sintassi (in SQL???) è la seguente:
begin transaction ... [lista operazioni] ... [commit | abort] end transaction
Le operazioni sono scritture, letture etc. etc., mentre la parte interessante è alla fine, il commit e l'abort. Il commit lo si ha quando tutto va OK, e la transazione è completata con successo. L'abort invece vuol dire che si è verificata qualche condizione di fallimento, e le operazioni vengono tutte annullate con un undo: si attua un rollback.
In una transazione ben formata, alla fine ci può essere solo uno dei due: o commit, o abort. E dopo questo, non ci deve essere nessun'altra operazione.
L'acronimo è ACID:
Una transazione è un'unità atomica, il che vuol dire che o l'eseguo tutta, o non eseguo niente. Non esistono stati intermedi. Un po' come i quanti della fisica. Se ci si ferma a metà, si disfa tutto e si fa l'UNDO di quanto già fatto. Se si ferma DOPO il commit, si fa il REDO.
Di solito invece funziona egregiamente e si va al commit felicemente. I casi di fallimento invece si possono distinguere in due tipi, amorevolmente così chiamati:
Tra aborti, suicidi ed omicidi, i Complementi di Basi di Dati sembrano più che altro Complementi di Genocidio di Massa. Potevano scegliere un linguaggio un po' meno cruento...
Quando le cose non vanno a buon fine, non deve rimanere nessun effetto di ciò che è andato male: questa è l'atomicità.
Una transazione non deve violare i vincoli di integrità.
I vincoli li posso controllare durante la transazione, o alla fine della stessa. Le due cose NON sono equivalenti. Basti pensare infatti ad una transazione in cui i valori, durante l'esecuzione, sforano temporaneamente i vincoli di integrità, per poi ritornare nella norma alla fine della transazione.
L'esecuzione di una transazione deve essere indipendente dalle altre transazioni eseguite contemporaneamente. Vuol dire che il risultato deve essere lo stesso sia nell'eseguire le trans in sequenza, che eseguirle in parallelo.
Gli effetti di una transazione non devono andare persi. Se una trans arriva al commit, è come quando il faraone ha parlato.
Queste simpatiche proprietà sono garantite dai moduli del DBMS, cioè parti del sistema informativo che presiedono al rispetto di ACID.
Nota sul Compilatore DDL. DDL = Data Description Language, ovvero il linguaggio che uso per descrivere i dati. Quando descrivo i miei bei dati, il compilatore provvede ad inserire dei controlli sui miei vincoli di integrità.
Si occupa di A e C. Esegue i comandi transazionali: B (begin), C (commit) e A (abort).
Se qualcosa non va, deve garantire (ancora questo verbo...) che il sistema venga ripristinato al suo stato corretto. E come faccio? Semplice, uso la MEMORIA STABILE!
Non che esista, ed infatti è un'astrazione: si tratta di quella memoria il cui contenuto non viene mai perso. Per approssimarsi a questa astrazione, si ricorre a vari metodi, tra cui quello di duplicare i dati in posti diversi, in modi diversi etc. così da minimizzare la probabilità che tutto vada perduto. La memoria stabile è quindi resa resistente ai guasti. Un guasto alla memoria stabile viene detto catastrofico, nonché definito impossibile.
Il Gestore dell'Affidabilità mantiene un log con su scritto tutto quanto fa. Questo log risiede in memoria stabile per resistere alla jella più nera e alle plugin più osé. Si tratta di un file in cui si tiene traccia di tutto quello che viene fatto dal DBMS, in ordine cronologico. Siccome tiene traccia di tutto, rende possibile il REDO e l'UNDO (vedremo poi).
Il log contiene 2 tipi di record:
Ma non registra solo il comando, bensì anche lo stato delle cose PRIMA (before state) e DOPO (after state) l'esecuzione del comando. Questo vuol dire che registra le transazioni non in modo relativo, ma in modo assoluto. Per intenderci, non scrive
x = 100 x = x + 100
ma
x = 100 x = 200
Salvandole in modo assoluto è possibile ripeterle o annullarle senza perdere informazioni.
Quando si vuola fare l'UNDO di un'azione che agisce su un oggetto O, si guarda il log di quell'azione:
Per il REDO:
Sia UNDO che REDO godono della proprietà di idempotenza, ovvero fare UNDO(A) (dove A è un'azione) per 1 volta o per 1000 volte è la stessa cosa. Uguale discorso per REDO(A): farlo 1 o 100 volte è uguale, il risultato non cambierà mai. Questa proprietà è resa possibile dal fatto che salvo i valori assoluti delle operazioni, e non quelli relativi.
Il checkpoint si fa periodicamente, e consiste nel bloccare tutte le operazioni in corse e proibirne l'accettazione di nuove. Nel log scrivo il record di checkpoint, in cui annoto tutte le operazioni in corso in quell'istante, e poi riprendo.
Il dump invece è una copia di tutta la base di dati, e quando la si fa, si ferma il tutto.
I record dei log vanno scritto rispettando 2 regole: write-ahead log e commit-precedenza.
Write-ahead log vuol dire che il BS va scritto PRIMA di effettuare le operazioni. Ciò permette l'UNDO in caso di rollback. Se scrivessi dopo, non avrebbe senso.
Commit-precedenza vuol dire invece che AS va scritto PRIMA di fare il commit, così è possibile fare il REDO.
Viene registrato anche il commit, nel suo bel record di commit così, grazie a queste regole, se c'è un guasto appena prima di esso, faccio un REDO. Se c'è un guasto dopo, faccio un UNDO grazie ai miei AS e BS.
Un altro problema che insorge è se attuare le operazioni sulla base di dati prima o dopo il commit. Se prima opero e poi committo, non serve il REDO. Se prima committo e poi opero, non serve UNDO. Ma se lavoro un po' così e un po' cosà allora servono sia REDO che UNDO.
Sono di due tipi:
Il log serve appunto per riparare i guasti di sistema, tramite un procedimento detto ripresa a caldo. Se invece ho un guasto di sistema, si attua la ripresa a freddo.
Il modello che si segue come reazione ai guasti si chiama fail-stop: se c'è un guasto o un errore, fermo tutto, avvio le procedure di ripristino, e se queste vanno bene riparto con l'attività normale. Se invece le procedure di ripristino non vanno a buon fine, torno nella fase di stop, e ripeto finché le cose non funzionano.
Ecco quello che succede in una ripresa a caldo.