Una transazione ACID è un gruppo di operazioni di database che fanno commit come un’unica unità: atomica, coerente, isolata e durevole. L’idea precede l’acronimo: Jim Gray ha definito le garanzie nel 1981, Härder e Reuter le hanno battezzate ACID nel 1983, e quarant’anni dopo resta il contratto che permette di spostare denaro nel software senza, di tanto in tanto, inventarne o distruggerne un po’.
Punti chiave
| Domanda | Risposta |
|---|---|
| Le quattro lettere | Atomica (tutto o nulla) · Coerente (le regole tengono) · Isolata (nessuna interferenza) · Durevole (sopravvive ai crash) |
| Esempio canonico | Il bonifico bancario: addebito + accredito fanno commit insieme o non lo fanno affatto |
| La manopola | I livelli di isolamento – prestazioni barattate con le anomalie di lettura |
| Il rivale | BASE / eventual consistency (coerenza finale) – disponibilità barattata con dati stantii |
| La realtà attuale | Anche i database documentali fanno ACID; le clausole in piccolo riguardano l’ambito |
Come funzionano le transazioni ACID in SQL (esempio di bonifico bancario)
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 'alice';
UPDATE accounts SET balance = balance + 100 WHERE id = 'bob';
-- Crash o errore tra i due UPDATE? Il motore fa rollback:
-- ROLLBACK; → entrambe le modifiche svaniscono; il denaro non può evaporare
COMMIT; -- oppure diventano permanenti entrambe, in modo atomico e durevole
Il codice applicativo incontra le stesse garanzie in pacchetti più piccoli – operazioni atomiche sui campi che chiudono la corsa read-modify-write, e batch che fanno commit insieme:
// JavaScript / Node.js — Back4app JS SDK
// Atomicity where apps actually need it
counter.increment('sold', 1); // atomic single-field update — no
await counter.save(); // read-modify-write race possible
// All-or-nothing batch: both rows commit, or neither does
await Parse.Object.saveAll([debitEntry, creditEntry], { transaction: true }); // Flutter / Dart — Back4app Flutter SDK
// Atomicity where apps actually need it
counter.setIncrement('sold', 1); // atomic single-field update — no
await counter.save(); // read-modify-write race possible
// Batches group writes; atomic counters remove the classic race
await ParseObject.saveAll([debitEntry, creditEntry]); // iOS / Swift — Back4app Swift SDK
// Atomicity where apps actually need it
var updated = counter
updated.sold = (updated.sold ?? 0) + 1
// Prefer the atomic operation over read-modify-write:
let op = counter.operation.increment("sold", by: 1)
op.save { result in
if case .success = result { print("atomic increment committed") }
} // Android / Kotlin — Back4app Android SDK
// Atomicity where apps actually need it
counter.increment("sold") // atomic single-field update — no
counter.saveInBackground() // read-modify-write race possible
// Batches group writes; atomic counters remove the classic race
ParseObject.saveAllInBackground(listOf(debitEntry, creditEntry)) Il ciclo di vita di una transazione: BEGIN, COMMIT e ROLLBACK
Le quattro proprietà, ciascuna definita da ciò che si rompe senza di essa: atomicità – senza, scritture parziali (il bonifico addebitato ma mai accreditato). Coerenza – senza, stati su cui si è fatto commit e che violano le regole dichiarate (giacenza negativa, riferimenti orfani). Isolamento – senza, transazioni concorrenti che leggono il lavoro mezzo finito l’una dell’altra. Durabilità – senza, dati “confermati” che un crash disfà in silenzio. Sotto il cofano, tre meccanismi le realizzano: gli undo log per il rollback, il write-ahead logging per sopravvivere ai crash, e i lock oppure MVCC per la concorrenza.
Livelli di isolamento vs. anomalie di lettura
La matrice che i risultati di ricerca non mettono mai insieme – quale livello ferma quale anomalia:
| Livello | Lettura sporca | Lettura non ripetibile | Lettura fantasma | Costo |
|---|---|---|---|---|
| Read uncommitted | possibile | possibile | possibile | il più basso |
| Read committed | impedita | possibile | possibile | basso |
| Repeatable read | impedita | impedita | possibile | medio |
| Serializable | impedita | impedita | impedita | il più alto |
Traduzione: una lettura sporca (dirty read) vede lavoro su cui non è stato fatto commit; una lettura non ripetibile ottiene risposte diverse chiedendo due volte; un fantasma vede comparire righe a transazione in corso. La maggior parte dei motori moderni adotta per impostazione predefinita un comportamento a snapshot basato su MVCC – ogni transazione legge uno snapshot coerente mentre chi scrive procede – ed è per questo che “chi legge non blocca chi scrive” è diventata la norma invece dell’eccezione.
ACID vs. BASE – e l’omonimia della coerenza
| Dimensione | ACID | BASE |
|---|---|---|
| Ottimizza per | La correttezza per transazione | La disponibilità su larga scala |
| Coerenza | Immediata, conserva le regole | Finale |
| Habitat naturale | Denaro, giacenze, prenotazioni | Feed, contatori, cache |
| Postura di scalabilità | Vincolata dal coordinamento | Orizzontale per progetto |
| Modo di degrado | Più lenta sotto contesa | Letture temporaneamente stantie |
Una sola disambiguazione regge tutto questo confronto: la C di ACID e la C di CAP sono due parole diverse che indossano la stessa lettera. La coerenza ACID significa che ogni transazione conserva le regole dichiarate. La coerenza CAP significa che tutti i nodi concordano in questo istante. Un database a nodo singolo è pienamente ACID senza che CAP entri nella stanza; un sistema distribuito sceglie il proprio trade-off CAP e le proprie garanzie transazionali separatamente.
Chi garantisce che cosa
| Famiglia di motori | Situazione rispetto ad ACID |
|---|---|
| PostgreSQL | ACID completo, MVCC, serializable disponibile |
| MySQL | ACID completo con InnoDB – la scelta del motore di archiviazione conta |
| SQLite | ACID completo, scrittore singolo, modalità WAL |
| MongoDB | Atomicità su singolo documento sempre; transazioni multi-documento dalla 4.0 (2018), isolamento snapshot |
| Motori SQL distribuiti | ACID via replica a consenso – serializable a prezzi di rete |
| Store wide-column / a coerenza finale | Regolabile, parziale – BASE per progetto |
Le transazioni distribuite si meritano la loro nota a piè di pagina onesta: il commit a due fasi (two-phase commit) compra l’atomicità tra nodi al prezzo della latenza e di un coordinatore bloccante, ed è per questo che le architetture a microservizi preferiscono sempre più le saga – sequenze di transazioni locali con rollback compensativi, che barattano la coerenza immediata con la disponibilità. Se un workflow davvero non tollera la compensazione, è la prova che il suo posto è dentro un solo database, non sparso su diversi.
Casi d’uso tipici
- Movimentazione di denaro. Bonifici, pagamenti, libri contabili – il caso canonico e ancora il più chiaro.
- Giacenze e prenotazioni. Scalare la giacenza e confermare l’ordine insieme, oppure guardare due clienti comprare l’ultimo posto.
- Invarianti su più righe. Ordine + righe d’ordine, conto + voce di audit – record validi solo se nascono insieme.
- Contatori fatti bene. Incrementi atomici – la dose utile più piccola di ACID – chiudono la corsa read-modify-write senza transazioni complete.
- Il complemento a coerenza finale. Feed, like, analisi: instradati esplicitamente lontano dal costo transazionale, per scelta.
Deve essere ACID? Matrice decisionale
| Pretendere transazioni complete quando… | La coerenza finale basta quando… |
|---|---|
| Denaro o proprietà passano di mano | Un conteggio stantio non costa nulla |
| Le scritture parziali creano stati non validi | Ogni scrittura è valida di per sé |
| Un ente regolatore chiederà conto dell’invariante | Il dato è derivato e ricostruibile |
| Due righe devono concordare, sempre | La convergenza differita è una UX accettabile |
| Vendere più del disponibile è una causa legale | Contare di troppo è un’alzata di spalle |
Il mestiere sta nell’instradamento per singola scrittura: il checkout usa le transazioni, il contatore delle visualizzazioni usa un incremento atomico, il feed tollera un secondo di scostamento – una sola applicazione, tre prezzi diversi per la coerenza.
Limiti e trade-off
- Il coordinamento è il centro di costo. I lock entrano in contesa, le sincronizzazioni del WAL toccano il disco, le transazioni serializable abortiscono e vanno ritentate – la correttezza ha un conto in throughput, che è l’intera ragione per cui BASE esiste.
- Il livello di isolamento è un’impostazione predefinita carica di conseguenze. La maggior parte dei motori non parte da serializable: conviene conoscere il livello effettivo, altrimenti le anomalie ritenute impossibili sono soltanto improbabili.
- Le transazioni lunghe sono un cattivo segno. Tenere i lock attraverso il tempo di riflessione di una persona o chiamate di rete trasforma le garanzie in contesa; le transazioni vanno tenute brevi e decise.
- L’ACID distribuito è caro per natura. Round di consenso a ogni commit – da pagare dove gli invarianti lo impongono, non in ogni punto per riflesso.
- ACID non valida le regole al posto di chi le scrive. La coerenza conserva i vincoli dichiarati; le regole di business mai codificate restano fedelmente non applicate.
Transazioni su Back4app
Back4app è una piattaforma open-source di Backend as a Service (BaaS) che combina database gestito, API REST e GraphQL generate automaticamente, autenticazione, archiviazione di file e funzioni serverless con Cloud Code. Il suo corredo transazionale rispecchia il modo in cui le app consumano davvero ACID: ogni scrittura su un singolo oggetto è atomica sul motore documentale sottostante, gli incrementi atomici e le operazioni sugli array chiudono le corse classiche sui contatori (le schede di codice qui sopra), i salvataggi in batch raggruppano le scritture, e gli invarianti a più passi stanno in Cloud Code – validati ed eseguiti lato server, dove un trigger beforeSave può rifiutare qualunque scrittura che infrangerebbe le regole. La matrice decisionale arriva già integrata: atomicità economica per impostazione predefinita, coordinamento completo dove lo si richiede.
Domande frequenti
Che cosa significa l'acronimo ACID?
Atomicità, coerenza, isolamento, durabilità – le quattro garanzie che una transazione di database deve dare perché i dati restino corretti attraverso errori, crash e utenti concorrenti. Jim Gray ha definito le proprietà fondamentali nel 1981; Theo Härder e Andreas Reuter hanno coniato l'acronimo nel loro articolo del 1983 sul recupero orientato alle transazioni.
Che cos'è una transazione ACID, in parole semplici?
Un gruppo di letture e scritture eseguite come un'unica unità tutto-o-nulla. O ogni operazione fa commit e diventa permanente, o ogni operazione fa rollback come se nulla fosse accaduto. L'esempio canonico è il trasferimento di denaro: si addebita un conto, se ne accredita un altro – un crash tra i due non deve mai lasciare il denaro addebitato ma non accreditato.
Che cosa sono i livelli di isolamento?
La manopola che baratta prestazioni per protezione quando le transazioni girano in concorrenza. Lo standard SQL ne definisce quattro – read uncommitted, read committed, repeatable read, serializable – e ciascuno impedisce più anomalie (letture sporche, letture non ripetibili, fantasmi) a un costo maggiore. In pratica la maggior parte dei motori moderni adotta per impostazione predefinita un isolamento in stile snapshot via MVCC, dove chi legge vede uno snapshot coerente e non blocca mai chi scrive.
Qual è la differenza tra ACID e BASE?
Due risposte al costo della correttezza. ACID paga in coordinamento per garantire che ogni transazione veda e lasci uno stato valido. BASE – Basically Available, Soft state, Eventually consistent – paga in obsolescenza temporanea per restare disponibile e scalare in orizzontale. Nessuno dei due è superiore: prezzano la coerenza in modo diverso, e i sistemi reali li mescolano in base al carico.
La coerenza di ACID è la stessa coerenza del teorema CAP?
No – e confonderle è l'equivoco più comune su questo argomento. La coerenza ACID significa che ogni transazione conserva le regole dichiarate: i vincoli tengono, gli invarianti sopravvivono. La coerenza CAP significa che ogni nodo di un sistema distribuito vede gli stessi dati nello stesso momento. Un database a nodo singolo può essere pienamente ACID, mentre CAP non ha nulla da dire al riguardo.
I database NoSQL sono conformi ad ACID?
Sempre di più, con clausole in piccolo. I database documentali hanno sempre reso atomiche le scritture su un singolo documento – cosa che, unita all'annidamento dei dati correlati, copre la maggior parte delle necessità di un'app. Le transazioni ACID multi-documento sono arrivate in MongoDB 4.0 (2018) con isolamento snapshot, a un costo prestazionale reale. La vecchia affermazione per cui "NoSQL significa nessuna transazione" è ormai semplicemente superata; la preferenza progettuale per modellare intorno all'atomicità del singolo documento no.
Come implementano davvero ACID i database?
Il carico lo portano tre meccanismi: le informazioni di undo rendono possibile il rollback (atomicità); il write-ahead logging – le modifiche registrate su un log durevole prima di essere applicate – sopravvive ai crash (durabilità); e i lock oppure MVCC tengono le transazioni concorrenti fuori dai piedi l'una dell'altra (isolamento). La coerenza è il risultato: i vincoli verificati dentro la protezione degli altri tre.
Quando basta la eventual consistency?
Quando una breve finestra di dati stantii non costa nulla: numero di like, contatori di visualizzazioni, feed di attività, analisi, cache, raccomandazioni di prodotto. Quando non basta: denaro, giacenze che si possono vendere oltre il disponibile, prenotazione di posti e biglietti, tutto ciò che è regolamentato. L'abilità ingegneristica non sta nello schierarsi, ma nell'instradare ogni scrittura verso la garanzia di cui ha davvero bisogno.