1.24k likes | 1.36k Views
Tecnologia di un Database Server (parte 2). S. Costantini Dispensa per il Corso di Basi di dati. Premessa. Questa dispensa integra i contenuti del Libro di Testo del Corso di Basi di dati.
E N D
Tecnologia di un Database Server (parte 2) S. Costantini Dispensa per il Corso di Basi di dati
Premessa • Questa dispensa integra i contenuti del Libro di Testo del Corso di Basi di dati Il materiale per queste slides è stato tratto in parte da (we acknowledge that this material is partly taken from):Phil Bernstein, Lecture Notes for a graduate course on Transaction Processing at the University of Washington,URL http://research.microsoft.com/~philbe/
Sommario • Locking e Lock Manager • Implementazione del Locking • Il Deadlock • Locking Gerarchico • Modello Matenatico del Locking • Ottimizzazioni • Gestore degli Accessi • Gestore dell’affidabilità • TP-Monitors • Basi di Dati e Web
Locking e Lock Manager S. Costantini
Database Modello di un Sistema Transazione 1 Transazione N Bot, SQL Queries Commit, Abort Ottimizzatore Query Esecutore Query Scheduler Buffer Manager Recovery manager Database Server
Locking • Obiettivo: assicurare schedule serializzabili delle transazioni • Implementazione: ritardare le operazioni che possono alterare la serializzabilita’ ponendo blocchi (locks) sui dati condivisi
Assunzione: operazioni atomiche • La Teoria del Controllo di Concorrenza considera le operazioni di Read e Write. • Quindi assume assume che esse siano atomiche • altrimenti, dovrebbe considerare le operazioni di piu’ basso livello che implementano Read e Write • Read(x) - restituisce il valore corrente di x nel DB • Write(x, val) sovrascrive x (l’intera pagina che lo contiene!) • Questa assunzione permette di astrarre l’esecuzione delle transazioni a sequenze di Read e Write
Locking • Ciascuna transazione pone un lock su ogni dato al quale intende accedere • il lock è una prenotazione • va effettuato prima di leggere/scrivere una dato • la transazione può ottenere il lock richiesto, oppure deve restare in attesa di ottenerlo • la transazione rilascia il dato dopo l’uso (unlock) • Ipotesi di lavoro: tutte le transazioni fanno commit, ossia nessuna fallisce (ipotesi di commit-proiezione)
Locking di base • indichiamo con: • rl(x) un read lock sul dato x • r(x) la lettura del dato x • wl(x) il lock in scrittura del dato x • w(x) la scrittura del dato x • ru(x) e wu(x) sono i relativi unlock • Con opi(x) indichiamo che l’operazione (una delle precedenti) è stata effettuata dalla transazione Ti
Locking di base • Stato possibile del dato x: • libero • read-locked (x è soggetto a lock condiviso) • write-locked (x è soggetto a lock esclusivo)
Gestione richieste di lock • rli(x) sempre concessa se x è libero o read-locked (più transazioni possono porre un lock condiviso) ma negata se x è write-locked. • wli(x) concessa solo se x è libero, ossia solo una transazione può scrivere un dato, escludendo tutte le altre.
Locking: sommario • Ciascuna transazione pone un lock su ogni dato al quale intende accedere • il lock e’ una prenotazione • ci sono read locks e write locks • se una transazione ha un write lock su x, allora nessun’altra transazione può avere alcun lock su x • Esempio • rli[x], rui[x], wli[x], wui[x] sono operazioni lock/unlock wl1[x] w1[x] rl2[x] r2[x] e’ impossibile • wl1[x] w1[x] wu1[x] rl2[x] r2[x] e’ OK
Gestione Richieste di Lock • Formato effettivo delle le operazioni di lock • Lock(trans-id, data-item-id, mode=r/w) • Unlock(trans-id, data-item-id) • Unlock(trans-id) • trans-id: identificatore della transazione • data-item-id: identificatore del dato x • mode = r/w indica se si tratta di un read o write lock
Gestione richieste di lock • Le transazioni che non ottengono un lock vengono poste in attesa. • Lo scheduler (o lock manager, o data manager) gestisce una tabella di lock • Tabella di lock: contiene le richieste accettate e rifiutate
Gestione richieste di lock • ogni riga della tabella di lock contiene: • il nome x del dato • l’elenco delle transazioni che hanno ottenuto un lock (indicando di che tipo) • l’elenco delle transazioni che sono state poste in attesa perchè non hanno ottenuto un lock (indicando di che tipo)
Lock manager • Il lock manager gestisce le operazioni • Lock(trans-id, data-item-id, mode=r/w) • Unlock(trans-id, data-item-id) • Unlock(trans-id) • Memorizza i lock nella lock table. Data Item Lista Locks Lista d’attesa x [T1,r] [T2,r] [T3,w] y [T4,w] [T5,w] [T6, r]
Il Locking di base non basta • Non garantisce la serializzabilita’ • rl1[x] r1[x] ru1[x] wl1[x] w1[x] wu1[x] c1 rl2[y] r2[y] wl2[x] w2[x] ru2[y] wu2[x] c2 • Eliminando i lock operations, abbiamo r1[x] r2[y] w2[x] c2 w1[x] c1 che non e’ serializzabile • Possibile soluzione: ogni transazione acquisisce tutti i lock all’inizio e li rilasci tutti alla fine • Problema: si limita troppo la concorrenza
Two-Phase Locking (2PL) Two-Phase locking: stesso effetto di tutti i lock all’inizio, ma meno vincoli sulla concorrenza • La transazione acquisisce man mano i lock necessari • richiede un lock solo quando deve operare su quel dato, e non all’inizio; • Nel frattempo effettua le operazioni sui dati; • Rilascia gradualmente i lock, con la condizione che dopo il primo unlock non può più fare lock
Two-Phase Locking (2PL) • Una transazione è two-phase locked (2PL) se: • prima di leggere x, pone un read lock on x • prima di scrivere x, pone un write lock on x • mantiene ciascun lock fino a dopo l’esecuzione dell’operazione sul dato • dopo il primo unlock, non può porre nuovi lock
Two-Phase Locking (2PL) • Una transazione acquisisce i lock durante la fase crescente e li rilascia nella fase calante • Esempio precedente: T2 e’ 2PL, ma non T1 perche’ ru1[x] precede wl1[x]
Rilasciare la Commit-proiezione • il 2PL si basa sulla commit-proiezione, ossia sull’ipotesi che tutte le transazioni facciano commit (e non abort) • Occorre ora rilasciare questa ipotesi • Se le transazioni possono fare abort, i problemi si complicano ed occorre porre condizioni addizionali al 2PL per evitare malfunzionamenti
Problemi • Se Tk legge da Ti e Ti fa abort, Tk deve fare abort • Esempio - w1[x] r2[x] a1 vuol dire che T2 deve fare abort • Ma se Tk ha gia’ fatto commit? • Esempio - w1[x] r2[x] c2 a1 • T2 non può fare abort dopo il commit
Recoverability • L’esecuzione deve essere recoverable:Il commit di una transazione T deve essere successivo al commit delle transazioni da cui legge. • Recoverable - w1[x] r2[x] c1 c2 • Non recoverable - w1[x] r2[x] c2 a1 • Si devono sincronizzare le operazioni
2PL e Recoverability • 2PL non garantisce la recoverability • Esempio: La seguente esecuzione è non-recoverable, ma è 2PL wl1[x] w1[x] wu1[x] rl2[x] r2[x] c2 … c1
Recoverability: Problemi • La recoverability garantisce la coerenza d’uso dei dati, ma genera aborti a catena • Gli aborti a caten cascading aborts (cosiddetto effetto domino) causano inefficienza perchè molte transazioni vengono uccise, perdendo il lavoro fatto.
Evitare Abort a cascata(cascading aborts) • Per evitare aborti a cascata, lo scheduler deve assicurare che le transazioni leggano solo da transazioni che hanno fatto commit • Esempio • evita cascading aborts: w1[x] c1 r2[x] • permette cascading aborts: w1[x] r2[x] a1 • Un sistema che evita cascading aborts garantisce la recoverability e si chiama sistema stretto (sistema che garantisce la strictness = no effetto domino)
Strictness • Un sistema stretto consente di eseguire ri[x] o wi[x] solo se tutte le transazioni precedenti che hanno scritto x hanno gia’ fatto commit o abort. • Esempi • stretto: w1[x] c1 w2[x] a2 • non stretto: w1[x] w2[x] … a1 a2 • stretto: w1[x] w1[y] c1 w2[y] r2[x] a2 • non stretto: w1[x] w1[y] … w2[y] a1 r2[x] a2 • “Stricness” evita “cascading aborts” (effetto domino)
Nota: Gestione dell’abort • Occorre annullare (undo) ogni write, w[x], ripristinando la before image (=il valore di x prima dell’esecuzione di w[x]) • Esempio - w1[x,2] scrive il valore “2” in x. • w1[x,2] w1[y,3] c1 w2[y,1] r2[x] a2 • abort T2 : si ripristina la before image of w2[y,2] = 3
Osservazione: Brain Concurrency Control • Se l’utente vuole usare l’output della transazione T1 come input per la transazione T2, deve aspettare che T1 faccia commit prima di lanciare T2. • Solo così e’ garantito che T1 venga effettivamente serializzata prima di T2. • Infatti, i sistemi serializzano secondo i criteri visti e non secondo le intenzioni dell’utente: se l’utente lancia prima T1 e poi T2 (ma prima che T1 termini) l’ordine fra esse non è garantito.
Locking Automatizzato • Nei sistemi stretti il 2PL puo’ essere reso trasparente per l’ applicazione • quando un data manager riceve una Read o Write da una transazione, emette un lock di read o write. • Come fa il data manager a sapere quando e’ ok rilasciare i locks (per essere two-phase)?
Locking Automatizzato • Come fa il data manager a sapere quando e’ ok rilasciare i locks (per essere two-phase)? • Di solito, il data manager mantiene i lock di ogni transazione finche’ essa fa commit o abort. In particolare: • rilascia i read locks appene riceve il commit • rilascia i write locks solo quando accetta il commit,per garantire la strictness
Implementazione del 2PL • Il data manager implementa il locking cosi’: • includendo un lock manager • generando un lock per ogni Read o Write • prevedendo la gestione dei deadlocks (vedi più avanti)
Lock manager • Il lock manager gestisce le operazioni • Lock(trans-id, data-item-id, mode=r/w) • Unlock(trans-id, data-item-id) • Unlock(trans-id) • Memorizza i lock nella lock table. Data Item Lista Locks Lista d’attesa x [T1,r] [T2,r] [T3,w] y [T4,w] [T5,w] [T6, r]
Come è rappresentato il dato x? • Dalla memoria secondaria non si trasferiscono mai singole tuple, ma “pezzi” più ampi, in genere pagine • Quindi, x si rappresenta: • indicando la pagina in cui si trova x • identificando x nella pagina (ad es. tramite la chiave)
Come è memorizzata la tabella? • Certo non sequenzialmente: troppo lento cercare i lock su x per accesso sequenziale. • Occorre una opportuna struttura dati • in generale: tabella hash • funzione di hash f applicata su x • f(x) identifica la riga di x nella tabella di locking ed è detto data-item-id
Lock manager • Il chiamante genera il data-item-id, applicando una funzione di hashing sul nome del dato • Infatti, la lock table e’ hashed sul data-item-id • Lock e Unlock devono essere atomici, quindi l’accesso alla lock table deve essere “locked” • Lock e Unlock vengono chiamati frequentemente. Devono essere molto veloci: < 100 istruzioni.
Lock manager (cont.) • In SQL Server 7.0 • I locks nella tabella occupano circa 32 bytes • Ogni lock contiene il Database-ID, Object-Id, e altre informazioni specifiche quali il record id (RID) o la chiave della tupla.
Deadlock • Un insieme di transazioni e’ in deadlock se ogni transazione e’ bloccata, e lo rimarra’ tutti’infinito se il sistema non interviene. • Esempio rl1[x] concesso rl2[y] concesso wl2[x] bloccata wl1[y] bloccata e deadlocked
Deadlock • Con il deadlock, 2PL evita le esecuzioni non serializzabili: • Ad es. rl1[x] r1[x] rl2[y] r2[y] … w2[x] w1[y] • oppure (caso della perdita di update) rl1[x] r1[x] rl2[x] r2[x] … w2[x] w1[x] • Per eliminare il deadlock: abort di una transazione • rilascio del lock senza abortire: si viola il 2PL
Prevenzione del Deadlock • Mai concedere un lock che puo’ portare al deadlock • Tecnica spesso usata nei sistemi operativi • Inutilizzabile nel 2PL, perche’ richiederebbe l’esecuzione seriale delle transazioni. • Esempio per prevenire il deadlock,rl1[x] rl2[y] wl2[x] wl1[y], il sistema nega rl2[y]
Prevenzione del Deadlock • Prevenire i deadlock non e’ praticabile in generale, perche’ pone eccessivi constraints alle applicazioni. • Porre tutti i lock in scrittura tutti all’inizio delle transazioni • Previene il deadlock perche’ la transazione “parte” solo se ha tutti i dati a sua esclusiva disposizione • riduce troppo la concorrenza.
Rilevazione del Deadlock • Si cerca di rilevare i deadlock automaticamente, e si abortisce una delle transazioni (la vittima). • E’ l’approccio piu’ usato, perche’ • consente una piu’ intensiva utilizzazione delle risorse
Rilevazione del Deadlock • timeout-based deadlock detection - Se una transazione resta bloccata per troppo tempo, allora abortiscila. • Semplice ed efficiente da implementare • Ma determina aborti non necessari e • Alcuni deadlocks persistono troppo a lungo
Rilevazione Tramite Waits-for Graph • Rilevazione esplicita dei deadlock tramite un grafo chiamato Waits-for Graph • Nodi = {transazioni} • Archi = {Ti Tk | Ti attende che Tk rilasci un lock} • Esempio precedente: T1 T2 • Teorema: Se c’e un deadlock, allora il Waits-for Graph ha un ciclo.
Rilevazione tramite Waits-for Graph (cont.) • Per rilevare i deadlocks • quando una transazione si blocca, aggiungi un arco • periodicamente cerca i cicli nel Waits-for Graph • Non occorre verificare troppo spesso. (Un ciclo non scompare finche’ non lo si “rompe”) • quando si trova un deadlock, si seleziona una vittima dal ciclo e la si abortisce. • Si seleziona una vittima che ha fatto poco lavoro (ad es., ha acquisito il numero minimo di lock).
Ripartenza Ciclica • Le transazioni possono ripartire e abortire all’infinito: mai uccidere la più vecchia • T1 inizia. Poi T2 inizia. • vanno in deadlock, e T1 (la piu’ vecchia) e’ abortita. • T1 riparte, T2 continua, e vanno ancora in deadlock • T2 (la piu’ vecchia) e’ abortita ... • Scegliere come vittima la transazione piu’ giovane evita la ripartenza ciclica, perche’ la piu’ vecchia non viene mai abortita, e può infine terminare. • Le due diverse euristiche si possono combinare
SQL Server 7.0 • Abortisce la transazione piu’ “economica” per il roll back. • La “piu’ economica” viene determinata in termini del numero di operazioni fatte (registrate nel file di log). • Permette il completamento delle transazioni che hanno fatto molto lavoro. • SET DEADLOCK_PRIORITY LOW (vs. NORMAL) permette ad un processo di designarsi da solo come vittima.
Locking distribuito • Supponiamo che una transazione possa accedere dati presso molti data managers • Ogni data manager tratta i lock nel modo usuale • Ogni transazione esegue commit o abort, nei confronti di tutti i data managers • Problema: deadlock distribuito