:: 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:
- correttezza
- robustezza
- isolamento
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.
Proprietà delle transazioni
L'acronimo è ACID:
- Atomicità
- consistenza
- Isolamento
- Durabilità = persistenza
Immagine accattivante per studenti
Atomicità
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:
- suicidio: è l'applicazione che decide il rollback
- omicidio: il rollback è deciso dal sistema
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à.
Consistenza
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.
Isolamento
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.
Durabilità = persistenza
Gli effetti di una transazione non devono andare persi. Se una trans arriva al commit, è come quando il faraone ha parlato.
Chi garantisce ACID?
Queste simpatiche proprietà sono garantite dai moduli del DBMS, cioè parti del sistema informativo che presiedono al rispetto di ACID.
- Gestore dell'affidabilità: si prende cura di A e P
- Compilatore DDL: C
- Gestore della concorrenza: I
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à.
Gestore Affidabilità
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:
- record di transazione, che contiene i comandi begin, insert, delete, update, commit, abort;
- record di sistema, che contiene i comandi dump e checkpoint. Il DUMP fa il backup completo del database, mentre il CHECKPOINT tiene traccia di tutte le transazioni in esecuzione in un dato istante di tempo.
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:
- UNDO di un UPDATE: copio il BS dell'azione nell'oggetto modificato
- UNDO di un DELETE: ripristino O con il valore BS
- UNDO di un insert: cancello O
Per il REDO:
- REDO di un UPDATE: copio AS in O
- REDO di un DELETE: cancello O
- REDO di un INSERT: reinserisco O
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.
Checkpoint e Dump
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.
Regole di scrittura dei record dei log
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.
Guasti
Sono di due tipi:
- di sistema: è un bug del software, salta la corrente o cose del genere. I miei dispositivi di memorizzazione non vengono rovinati.
- di dispositivo: si sfasciano i dispositivi che ospitano i miei dati, e qui è più brutta.
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.
Ripresa a caldo
Ecco quello che succede in una ripresa a caldo.
- Ripercorro il file di log all'indietro fino all'ultimo checkpoint effettuato
- Creo due insiemi: l'insieme REDO e l'insieme UNDO.
- L'insieme REDO all'inizio è vuoto
- L'insieme UNDO viene riempito con tutte le transazioni che il checkpoint mi indica come attive
- Percorro il log dal checkpoint in avanti, ed eseguo una di queste tre operazioni:
- tutte le transazioni che iniziano ma non hanno un commit le metto nell'insieme UNDO
- sposto da UNDO a REDO tutte le transazioni di cui trovo il commit
- metto nell'insieme REDO tutte le transazioni iniziate dopo il checkpoint e che hanno il commit registrato prima del crash
- Vado nel log al BOT (Begin Of Transaction) della transazione più vecchia che ho nei due insiemi REDO e UNDO
- Vado verso la fine del log ed eseguo tutte le operazioni delle transazioni che compaiono nei miei due insiemi.
Ripresa a freddo
- Vado all'ultimo DUMP e ripristino tutto il database copiandocelo sopra
- Vado indietro nel log fino al record dell'ultimo DUMP ed eseguo tutte le azioni indicate
- Eseguo una ripresa a caldo
Torna alla pagina di Basi di Dati - Complementi