Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Transazioni e gestione delle richieste concorrenti

Effettuare insiemi di operazioni, anche concorrenti, garantendo l'integrità del database
Effettuare insiemi di operazioni, anche concorrenti, garantendo l'integrità del database
Link copiato negli appunti

Qualunque richiesta ad un DBMS è a tutti gli effetti una transazione. Una transazione può essere interpretata come un insieme di operazioni di lettura e di scrittura sul database e costituisce l'effetto di una serie di funzioni effettuate dagli utenti.

In un database relazionale la modalità standard adottata per l'avvio e la conclusione di una transazione viene chiamata "Non Concatenata" o Autocommit. In questo tipo di approccio ogni query o aggiornamento avvia e conclude una nuova transazione. Questo è il caso di una interrogazione semplice o del richiamo di una certa vista sul DB.

Nel caso in cui abbiamo bisogno di effettuare più operazioni sul DB dipendenti tra loro, quando, ad esempio, il successo di un'operazione può compromettere a cascata quello delle precedenti, dobbiamo definire la transazione in modo più strutturato. Le operazioni devono essere eseguite in un azione atomica, non suddivisibile, bisogna che l'utente avvii e concluda la stessa utilizzando la modalità Concatenata.

Questo tipo di approccio prevede la dichiarazione dell'inizio e della fine del blocco di istruzioni. Con il comando BEGIN TRANSACTION indichiamo l'inizio della transazione.

BEGIN TRANSACTION [transaction_name]

Il blocco di istruzioni si concluderà poi con l'esito della transazione, che può essere

  • positivo in questo caso si usa l'istruzione COMMIT e significa che tutte le istruzioni del blocco sono state eseguite con successo
  • negativo in questp caso si usa ROLLBACK, ovvero si riavvolge il nastro fino a far ritornare il database nello stato precedente all'inizio della transazione. Questo si verifica se anche una delle operazioni non va a buon fine.

L'istruzione di COMMIT ha l'effetto di rendere permanente le operazioni solo quando quest'ultime sono corrette. In caso contrario il sistema le annulla come se l'utente avesse chiamato l'istruzione di ROLLBACK.

Ecco le situazioni in cui si verifica un ROLLBACK:

Errori Logici
Quando vengono immessi dati errati o violati vincoli di integrità.
Fallimenti Voluti
Quando il programmatore la invoca esplicitamente.
Forzature del Sistema
Quando il DBMS impone il fallimento di una transazione per sbloccare una situazione di "stallo".
Crash del Sistema o Guasti dell'hardware
Quando ci sono problemi relativi all'hardware o c'è un blackout elettrico.

Proprietà delle Transazioni

La base di dati è una "risorsa condivisa" da più applicazioni e quindi è soggetta ad accessi concorrenti. Per rendere una transazione stabile, isolata e consistente essa deve rispettare le seguenti proprietà dette "ACID" (proprietà acide):

Atomicity - Atomicità
Dopo essere stata avviata, una transazione può terminare con successo, può abortire o può essere abortita dal DBMS dopo l'esecuzione di tutte o alcune delle sue operazioni. Ogni transazione deve essere atomica (non suddivisibile) e quindi deve eseguire tutte le sue azioni o non deve eseguirne alcuna. Il DBMS controlla la sequenza di azioni registrandole (nel "database log file") in modo che ogni qual volta una transazione deve essere abortita le sue azioni vengono annullate.
Consistency - Consistenza
Questa proprietà deve garantire che una transazione non violi i vincoli di integrità. Una transazione rappresenta una trasformazione corretta dello stato del database e quindi al suo termine quest'ultimo deve trovarsi in uno stato consistente senza che si verifichino contraddizioni (inconsistency) tra i dati archiviati.
Isolation - Isolamento
Una transazione non può leggere risultati intermedi di altre transazioni e quindi deve sempre "osservare" una base di dati consistente. Questo tipo di proprietà deve garantire che gli effetti di una transazione siano isolati fino al termine della stessa. Per questo ogni transazione deve essere eseguita indipendentemente dall'esecuzione delle altre transazioni.
Durability - Durabilità
Nel caso in cui un operazione abbia modificato la base di dati i suoi effetti devono essere permanenti. Questo deve avvenire in tutti i casi anche in caso di errori eccezionali.

L'atomicità e la durabilità sono garantite dal "Reliability/Recovery Manager" (Gestore dell'Affidabilità), l'Isolamento dal "Gestore della Concorrenza" e la Consistenza dal "Gestore dell'integrità a tempo di esecuzione".

Problematiche relative alla Concorrenza

La programmazione concorrente è sempre stato un problema in informatica e si è evidenziato nei sistemi operativi (con i processi che condividono memoria centrale) e nei linguaggi di programmazione (con i thread che condividono la memoria heap).

Le problematiche che si possono verificare in casi del genere sono le seguenti:

Race Contitions - Corse Critiche
Un fenomeno di corsa si verifica quando più processi effettuano concorrentemente operazioni di aggiornamento su una stessa risorsa. Per superare questo problema viene ritardata l'esecuzione di operazioni in conflitto imponendo alle transazioni di richiedere dei blocchi (detti lock) per poter effettuare le operazioni di lettura e scittura. Una
porzione di codice, che può essere eseguita solamente da un processo, viene detta "Sezione Critica". Prima di ogni Sezione Critica una tranzazione acquisisce i lock sul "codice critico" per poi rilasciarli quando la Sezione Critica termina.
DeadLock - Morte per Stallo
In questo tipo di problematica due o più processi che vogliono accedere ad una risorsa condivisa attendono tutti che un processo rilasci il controllo di una Sezione Critica così da bloccarsi vicendevolmente. In questo caso si dice che le transazioni sono poste in "mutua attesa" di risorse bloccate. Per gestire questo problema, si può adottare una
politica mirata di allocazione delle risorse o una allocazione non controllata seguita dall'aborto delle cause, per poter liberare le risorse.
Starvation - Morte per Deprivazione
In questo tipo di problema un processo non riesce mai ad ottenere le risorse di cui necessita. Questa problematica è dipesa principalmente dalla priorità di accesso alla risorsa che il processo possiede. Può essere evitata con "tecniche di invecchiamento" che prevedono l'aumento progressivo e regolano la "priorità di accesso" del processo.

Il DBMS può trovare una soluzione a queste problematiche nei servizi di sincronizzazione che offre ogni sistema operativo ma non lo fa per due motivi principali:

Concessione di blocchi esclusivi
Per ragioni di prestazioni è necessario utilizzare lock di granularità più fine. È necessario distinguere "lock in scrittura" e "lock in lettura" perchè se un dato deve essere aggiornato non si possono concorrentemente eseguire né operazioni di scrittura e né operazioni di lettura. Ne consegue che nelle tecniche di controllo reimplementate da
un database "lock in scrittura" agiscono in modo "esclusivo" senza permettere l'accesso ad un dato da parte di altri, mentre "lock in lettura" permettono altre letture per non inibire completamente l'accesso ad un dato.
Garantire la Durabilità
Una transazione deve essere riproducibile e annulabile in caso di guasti. Il Reliability/Recovery Manager gestisce l'esecuzione delle operazioni di rispristino.

Quindi, il DBMS, per evitare che si verifichino delle anomalie, deve forzatamente implementare un modulo relativo alla gestione della concorrenza che eseguirà le transazioni garantendo il rispetto delle proprietà acide ed in particolar modo della proprietà dell'isolamento.

Anomalie delle Transazioni in un DBMS

Prima di analizzare la gestione della concorrenza in un DBMS dobbiamo rilevare, più in dettaglio, le possibili interferenze che si possono creare nell'esecuzione di diverse transazioni. Prendendo in considerazione due ipotetiche transazioni T1 e T2 che agiscono sul database avremo le possibili anomalie riguardanti il loro accesso concorrente:

Write-Write - Lost Update : Perdite di Aggiornamento
  1. T1 effettua un UPDATE del dato Y.
  2. T2 effettua nuovamente un UPDATE dello stesso dato Y.
  3. La transazione T1 viene abortita a causa di un errore e va in ROLLBACK portando il database allo stato di origine.
  4. La transazione T2 va in COMMIT e termina con successo.

Questo tipo di anomalia causa la perdita dell'aggiornamento della transazione T2.

Read-Write - Unrepeatable Read : Letture non riproducibili
  1. T1 effettua una SELECT sul dato Y.
  2. T2 effettua un UPDATE dello stesso dato Y e va in COMMIT.
  3. La transazione T1 rieffettua una SELECT nello stesso dato Y senza effettuare una modifica.

In questo caso T1 effettua due SELECT che restituiscono valori diversi di Y perchè la transazione T2, essendo concorrente, ne ha modificato il risultato durante l'esecuzione.

Write-Read - Dirty Read : Letture Fantasma
  1. T1 effettua un UPDATE sul dato Y.
  2. T2 effettua una SELECT dello stesso dato Y.
  3. La transazione T1 abortisce andando in ROLLBACK.

In quest'ultimo caso,il dato letto dalla transazione T2 non sarà valido perchè non è persistente nel database.

Gestione della Concorrenza

Per gestire queste anomalie bisogna aumentare il livello di isolamento che automaticamente diminuisce e regola il grado di concorrenza che un determinato dato può ottenere. I DBMS prevedono la possibilità di rilasciare i vincoli di isolamento tramite il concetto di "serializzabilità". Il DBMS nel rispettare questo concetto adotta un sistema di lock a livello di record (e non di tabella come nei sistemi operativi). I blocchi adottati sono:

Shared Locks - Blocchi Condivisi
Vengono richiesti in caso di lettura di un record e la loro acquisizione può essere concorrente ad altre acquisizioni dello stesso tipo
Exclusive Lock - Blocchi esclusivi
Vengono richiesti in caso di scrittura di un record e sono eseguiti in isolamento

Per la gestione delle acquisizioni di queste due tipologie di blocchi il DBMS implementa una strategia di "concessione dei lock" chiamata "Strict Two Phase Locking".

Il protocollo Strict 2PL stabilisce che:

  1. Ogni transazione deve ottenere uno Shared Lock su un oggetto prima di effettuare una lettura
  2. Ogni transazione deve ottenere un Exclusive Lock su un oggetto prima di effettuare una scrittura
  3. Se una transazione ha acquisito un Exclusive Lock su un oggetto nessuna altra transazione può acquisire Lock di qualsiasi tipo su quell'oggetto.
  4. Ogni Lock acquisito da una transazione deve essere rilasciato al suo termine

Lo Strict 2PL permette solo esecuzioni seriali delle transazioni, snellisce l'esecuzione di aborto delle transazioni e risolve i problemi relativi alle perdite di aggiornamento (Lost Update) e alle letture non riproducibili (Unrepeatable Read).

Il problema relativo alle letture fantasma (Dirty Read) viene risolto adottando un'altra tipologia di Lock che nell'arco della durata di una transazione non permette la modifica del numero degli elementi che soddisfano le condizioni. I Lock vengono adottati sui predicati utilizzati nelle clausole WHERE e in concreto vengono acquisiti per via della presenza dei livelli di isolamento che il DBMS mette a disposizione.

I livelli di isolamento previsti sono i seguenti:

READ UNCOMMITED
Con questo livello non si possono verificare solo Lost Update. Consente transazioni in sola lettura, senza bloccare la lettura dei dati.
READ COMMITTED
Con questo livello di isolamento si possono verificare Unrepeatable Read ma non Dirty Read. Con il suo utilizzo è previsto il rilascio immediato dei dati in lettura e il rilascio ritardato dei dati in scrittura.
REPEATABLE READ
Con questo livello si possono verificare solo Dirty Read. In pratica rappresenta lo Strict 2PL. Utilizzandolo vengono bloccati sia i dati in lettura che in scrittura dei record coinvolti.
SERIALIZABLE
Grazie a questo livello si raggiunge un isolamento perfetto evitando le anomalie viste.

Il livello SERIALIZABLE non risolve tutti i problemi: la sua dichiarazione richiede un numero elevato di blocchi e aumenta la competizione tra le transazioni per l'acquisizione di blocchi. Questo peggiora le prestazioni e aumenta drasticamente il rischio di stallo. La migliore soluzione sta nell'utilizzare un pò tutti i livelli di isolamento in base alle esigenze del dato o dell'applicazione.

Ti consigliamo anche