Questo articolo utilizza parte delle informazioni trovate in:

1.Introduzione

Una transazione รจ detta una sequenza di operazioni effettuate da una singola unitร  logica di lavoro.

Se la transazione viene eseguita correttamente, viene eseguito il commit di tutte le modifiche dei dati apportate durante la transazione e tali modifiche diventano parte permanente del database. Se si verificano errori durante lโ€™esecuzione della transazione ed รจ necessario annullarla o eseguirne il rollback, verranno cancellate tutte le modifiche dei dati.

Una transazione deve avere tutte le quattro proprietร  chiave dellโ€™acronimo ACID:

  • Atomicitร : la transazione รจ indivisibile nella sua esecuzione e la sua esecuzione deve essere o totale o nulla, non sono ammesse esecuzioni parziali;
  • Coerenza: quando inizia una transazione il database si trova in uno stato coerente e quando la transazione termina il database deve essere in un altro stato coerente, ovvero non deve violare eventuali vincoli di integritร , quindi non devono verificarsi contraddizioni (inconsistenza) tra i dati archiviati nel DB;
  • Isolamento: ogni transazione deve essere eseguita in modo isolato e indipendente dalle altre transazioni, lโ€™eventuale fallimento di una transazione non deve interferire con le altre transazioni in esecuzione;
  • Durabilitร : una volta che una transazione abbia richiesto un commit work, i cambiamenti apportati non dovranno essere piรน persi.

2. Livelli di isolamento

Sono previsti quattro livelli di isolamento, qui elencati dal meno al piรน sicuro.

  • Read uncommitted: consente transazioni in sola lettura, senza bloccare in lettura i dati, conseguentemente una transazione puรฒ leggere dati sporchi, perchรฉ potrebbero sparire se la transazione che li ha generati abortisce. Vedi approfondimento qui;
  • Read committed: prevede il rilascio immediato dei dati in lettura, ritardando quelli in scrittura, conseguentemente previene il fenomeno delle letture sporche;
  • Repeatable read: vengono bloccati sia i dati in lettura che quelli in scrittura ma solo sulle ennuple della tabella coinvolte. Questa modalitร  previene la lettura dei dati quando altre transazioni stanno modificando gli stessi, ma non previene operazioni di INSERT, cioรจ dati che vengono aggiunti nella transazione corrente da altre transazioni, conseguentemente ottengo un numero diverso di righe lanciando la stessa query due volte allโ€™interno della stessa transazione. Queste letture vengono dette phantom reads;
  • Serializable: il piรน alto livello di sicurezza delle transazioni, con lโ€™utilizzo dei RANGE lock previene la lettura, la modifica e lโ€™inserimento di nuovi dati fino a che la transazione corrente non รจ stata correttamente completata.

3. Tipologie di istruzioni

3.1 Transazioni con autocommit

La piรน semplice transazione รจ una singola istruzione di modifica dati, come la seguente:

UPDATE Autori
SET Nome = 'John'
WHERE IdAutori = 10

Questo tipo di transazione รจ chiamato Autocommit transaction, SQL Server esegue questa serie di operazioni:

  1. Scrive in un file di log cosa sta per fare;
  2. Effettua effettivamente lโ€™istruzione di update;
  3. Scrive nel file di log che ha completato lโ€™operazione.

Se il server fallisce dopo che una transazione รจ stata scritta nel file di log. SQl Server utilizzerร  tale log per eseguire un roll forward di tale transazione quando questa ricomincerร .

3.2 Transazioni esplicite

Per rendere le transazioni piรน utili dobbiamo inserire due o piรน istruzioni allโ€™interno di queste. Queste transazioni sono chiamate Explicit Transaction, per esempio:

BEGIN TRANSACTION 
 
UPDATE Autori
SET Nome = 'John'
WHERE IdAutori = '10'
 
UPDATE Autori
SET Nome = 'Marg'
WHERE IdAutori = '8'
 
COMMIT TRANSACTION

Dato che la transazione รจ esplicita, abbiamo un BEGIN TRANSACTION allโ€™inzio del set di istruzioni ed un COMMIT TRANSACTION alla fine.

  • BEGIN TRANSACTION Contrassegna il punto di inizio di una transazione locale esplicita ed incrementa la funzione @@TRANCOUNT di una unitร .
  • COMMIT TRANSACTION: Contrassegna la fine di una transazione esplicita o implicita completata correttamente. Se il valore di @@TRANCOUNT รจ 1, COMMIT TRANSACTION rende permanenti nel database tutte le modifiche dei dati apportate dallโ€™inizio della transazione, libera le risorse mantenute attive dalla transazione e decrementa il valore di @@TRANCOUNT a 0. Se il valore di @@TRANCOUNT รจ maggiore di 1, COMMIT TRANSACTION decrementa il valore di @@TRANCOUNT di una sola unitร  e la transazione rimane attiva.

Ogni istruzione allโ€™interno dei due comandi รจ considerato come una singola unitร  logica di lavoro. Se il sistema fallisce per un qualsiasi motivo dopo il primo update, nessun UPDATE verrร  eseguito. Il file di log conterrร  un BEGIN TRANSACTION ma nessun COMMIT TRANSACTION . Al posto del COMMIT posso esplicitare un ROLLBACK TRANSACTION, che esegue il rollback di una transazione implicita o esplicita fino allโ€™inizio della transazione o fino a un punto di salvataggio. Lโ€™istruzione ROLLBACK TRANSACTION consente di cancellare tutte le modifiche dei dati eseguite dallโ€™inizio della transazione o fino a un punto di salvataggio, nonchรฉ di liberare le risorse utilizzate dalla transazione.

3.2.1 Stored Procedure

La maggior parte delle transazioni avverranno allโ€™interno di Stored Procedure. Consideriamo il seguente esempio:

CREATE PROCEDURE TranTest1
AS
BEGIN TRANSACTION
 
-- idAutori=10 esiste giร  su DB
INSERT INTO Autori(idAutori, Nome, Cognome)
VALUES (
10
, 'Gates'
, 'Bill'
)
 
UPDATE Autori
SET Nome = 'Johnzzz'
WHERE idAutori = 10
 
COMMIT TRANSACTION

Il problema con questa Stored Procedure รจ che la transazione indicata non verifica che le operazioni siano andate a buon fine o meno, verifica solo che SQL Server non fallisca tra lโ€™INSERT e lโ€™UPDATE.

Lanciando questa SP, questa proverร  ad inserire un record duplicato nella tabella Autori, che fornirร  un errore di violazione di chiave primaria; inoltre il messaggio avviserร  che the statement has been terminated.

Il problema รจ che la transazione รจ ancora aperta, conseguentemente lโ€™UPDATE verrร  eseguito correttamente e SQL Server eseguirร  il COMMIT della transazione, comportamento assolutamente non voluto.

Il modo corretto di scrivere questa SP รจ il seguente:

CREATE PROCEDURE TranTest1
AS
BEGIN TRANSACTION
 
INSERT INTO Autori (
idAutori
, Nome
, Cognome
)
VALUES (
10
, 'Gates'
, 'Bill'
)
 
IF @@ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
 
RETURN 10
END
 
UPDATE Autori
SET Nome = 'John'
WHERE idAutori = 10
 
IF @@ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
 
RETURN 11
END
 
COMMIT TRANSACTION

In questo modo abbiamo il controllo di ogni istruzione per verificare se questa fallisca o meno. Se lโ€™istruzione in question fallisce (che posso verificare tramite il controllo che @@ERROR <> 0) allora eseguiamo un ROLLBACK di quanto eseguito e forniamo un RETURN che esce dalla stored procedure.

Sottolineiamo ancora che se non controlliamo gli errori dopo ogni istruzione atomica allโ€™interno della transazione, potremmo eseguire impropriamente un COMMMIT di tale transazione.

3.3 Transazioni implicite

Una nuova transazione viene avviata in modo implicito al termine di una transazione precedente, conseguentemente non devo scrivere nulla per indicare lโ€™inizio di una transazione, ma tutte le transazioni vengono terminate in modo esplicito con unโ€™istruzione COMMIT o ROLLBACK. Questa รจ una modalitร  di lavoro che deve essere impostata dal Database Engine utilizzato.

4. Transazioni innestate

SQL Server permette lโ€™utilizzo di transazioni innestate: questo significa che una nuova transazione puรฒ iniziare anche se la precedente non รจ stata ancora completata.

Questo viene eseguito semplicemente innestando un BEGIN TRANSACTION prima di un COMMIT o ROLLABCK; lโ€™indicatore @@TRANCOUNT indica il numero di transazioni attive in questo momento, quindi il livello di nesting. Andiamo ora ad analizzare il comportamento dei comandi in base al livello di nesting a cui questi vengono lanciati

4.1 COMMIT

Il comando di COMMIT ha due comportamenti diversi nel caso in cui questo sia lanciato nella transazione piรน esterna o meno. Se viene lanciato nellโ€™ultima transazione, allora effettua le scritture effettive su disco, altrimenti si limita a diminuire il valore di @@TRANCOUNT di uno. Conseguentemente una transazione non รจ mai commitatta definitivamente fino a che lโ€™ultimo COMMIT non รจ stato lanciato.

4.2 ROLLBACK

Il comando di ROLLBACK invece lavora independentemente dal livello di nesting a cui viene lanciato ed esegue un ROLLBACK di tutte le transazioni.

4.3 Lโ€™asimmetria tra COMMIT e ROLLBACK

Anche se sembra controintuitivo, esiste una ottima motivazione per cui SQL Server lavora in questo modo: se un COMMIT innestato effettuasse veramente le scritture su disco, tutti i ROLLBACK esterni non potrebbero effettuare effettivamente il ripristino di questi cambiamenti in quanto questi sarebbero giร  registrati in modo permanente.

Questa modalitร  รจ detta asimmetria tra COMMIT E ROLLBACK. Consideriamo il seguente esempio:

-- @@TRANCOUNT = 0
BEGIN TRANSACTION
-- @@TRANCOUNT = 1
DELETE table_1
BEGIN TRANSACTION transaction_name
-- @@TRANCOUNT = 2
DELETE table_2
COMMIT TRANSACTION nested
-- Esegue solo una diminuzione di @@TRANCOUNT di uno
-- @@TRANCOUNT = 1
ROLLBACK TRANSACTION
-- @@TRANCOUNT = 0
 
SELECT * FROM table_2

In questo esempio possiamo notare che il comando di ROLLBACK ripristina i valori della tabella table_2 anche se รจ stato eseguito un COMMIT su di essa.

4.4 SAVE TRANSACTION

I savepoint sono un meccaniscmo per eseguire il ROLLBACK solo di una porzione di una transazione. I savepoint definiscono un punto nel codice in cui una transazione puรฒ ritornare se parte di una transazione viene annullato. SQL Server permette di identificare un savepoint mediante il comando SAVE TRANSACTION, che non aumenta il valore di @@TRANCOUNT, inoltre un ROLLBACK ad un savepoint non modifica il valore di @@TRANCOUNT (a differenza di un ROLLBACK ad una transazione). Unโ€™istruzione di ROLLBACK deve esplicitare il nome del savepoint, altrimenti verrร  eseguito il rollback dellโ€™intera transazione.