Una guida alle proprietà ACID nei sistemi di gestione dei database
L'uso di applicazioni in tempo reale e dispositivi Internet delle cose (IoT), oltre alla crescita esponenziale degli asset di dati non strutturati, ha spinto un numero sempre maggiore di organizzazioni a passare ai database NoSQL. Infatti, si prevede che il mercato NoSQL raggiungerà i 74,5 miliardi di dollari entro il 2032, registrando un tasso di crescita (CAGR) del 24,9% dal 2024 al 2032 (gruppo IMARC, 2024).
Questa crescita non sorprende, data la capacità dei sistemi di gestione dei database (DBMS) NoSQL di gestire efficacemente set di dati ampi e diversificati e la relativa analisi dei big data richiesta.
Tuttavia, molti credono ancora che i DBMS NoSQL non siano in grado di soddisfare un requisito fondamentale di molte organizzazioni: la gestione delle transazioni ACID e la conformità. La buona notizia è che alcuni DBMS NoSQL possono farlo.
In questo articolo parleremo delle transazioni ACID, delle loro proprietà, della ragione per cui queste transazioni sono importanti e di un esempio di transazione ACID in un DBMS NoSQL.
Sommario
- Breve storia delle transazioni ACID
- Che cosa sono le transazioni ACID?
- Perché le transazioni ACID sono importanti?
- Esempio di transazioni ACID nei DBMS
- Come funzionano le transazioni ACID in MongoDB?
- Quando si devono utilizzare le transazioni multi-documento di MongoDB?
- Quali sono le best practice per le transazioni in MongoDB?
- Domande frequenti
Breve storia delle transazioni ACID
Il concetto di transazione ACID è stato fondamentale per l'evoluzione dei moderni sistemi di database dalla fine degli anni '70.
Man mano che le aziende e le istituzioni di ricerca iniziavano a fare maggiore affidamento sui dati digitali, è diventata chiara l'esigenza di disporre di modi affidabili per elaborare operazioni complesse su più record. I primi relational database come System R di IBM e Oracle furono all'avanguardia nella sperimentazione del consolidamento di garanzie più rigorose in materia di integrità dei dati e affidabilità delle transazioni.
Nel 1983, gli informatici Andreas Reuter e Theo Härder definirono formalmente le quattro proprietà ACID (atomicità, coerenza, isolamento e durabilità) in un documento fondamentale. Il loro lavoro ha fornito alla comunità dei database un linguaggio comune per descrivere i requisiti per transazioni di database affidabili. Ogni proprietà intendeva affrontare una sfida specifica nel garantire che i dati rimanessero validi e venissero elaborati in modo affidabile, anche quando si verificavano errori:
- L'atomicità garantisce che molteplici operazioni in una transazione agiscano come un'unica unità: o hanno tutte esito positivo o hanno tutte esito negativo.
- La coerenza assicura che il database rimanga in uno stato valido che aderisce alle regole e ai vincoli definiti.
- L'isolamento impedisce alle transazioni simultanee di interferire tra loro.
- La durabilità garantisce che, una volta completata la transazione, i risultati siano permanenti, anche dopo guasti del sistema.
Questo framework è diventato il gold standard per i sistemi di gestione di relational database (RDBMS), soprattutto quando le aziende li hanno adottati per i settori bancario, retail e delle telecomunicazioni, settori in cui le transazioni finanziarie e la conformità normativa richiedono un'assoluta accuratezza.
Nel corso degli anni '90 e dei primi anni 2000, i relational database come Oracle, IBM DB2 e Microsoft SQL Server hanno continuato a perfezionare il modo in cui venivano implementate le garanzie ACID, spesso utilizzando tecniche come il write-ahead logging, i lock e il multi-version concurrency control.
Questi metodi hanno consentito alle organizzazioni di elaborare milioni di operazioni di database mantenendo una coerenza rigorosa.
Con l'avvento dei database NoSQL negli anni 2010, alcuni si sono chiesti se le garanzie ACID potessero essere preservate nei sistemi progettati per la scalabilità orizzontale e l'alta disponibilità. Spesso le prime piattaforme NoSQL riducevano la coerenza per garantire migliori prestazioni e disponibilità, portando a presumere che l'ACID compliance e la scalabilità NoSQL si escludessero a vicenda.
Tuttavia, con la crescente richiesta di integrità dei dati nei sistemi distribuiti, fornitori come MongoDB hanno dimostrato che era possibile supportare le transazioni multi-documento continuando a scalare su più nodi.
Oggi le transazioni ACID rimangono più rilevanti che mai. Forniscono le basi per mantenere l'integrità dei dati nei settori in cui gli errori sono inaccettabili, dai trasferimenti di conti bancari alle cartelle cliniche.
Al contempo, piattaforme moderne come MongoDB hanno esteso le garanzie ACID ai dati flessibili e non strutturati e alle operazioni complesse nei cluster distribuiti. Questa combinazione di affidabilità e scalabilità garantisce che i principi ACID, definiti per la prima volta oltre 40 anni fa, continuino a guidare il futuro della gestione dei database.
Che cosa sono le transazioni ACID?
Transazioni in dettaglio
A un livello base, le transazioni del database sono un insieme di operazioni di lettura e scrittura sul database che sono state completate con successo in base alle definizioni del DBMS (ad esempio, criteri di transazione definiti). Esistono due tipi principali di transazioni:
- Transazioni singole: una transazione singola è una serie di una o più operazioni di database che si traducono in un'unica azione, completata con successo. Una volta completata, la transazione viene accettata e può essere consultata nel log delle transazioni. Un esempio comune di transazione singola è il prelievo di denaro da uno sportello bancomat.
- Multi-transazioni: una multi-transazione, talvolta detta transazione distribuita, è composta da più transazioni interdipendenti distribuite su diversi database e sistemi (ad esempio, sistemi distribuiti). I record di queste transazioni si trovano anche in un log delle transazioni. Un esempio di queste transazioni è, ad esempio, il trasferimento di denaro da un conto all'altro o il rilascio di un badge di sicurezza con foto a un nuovo dipendente.
È importante notare che alcune transazioni devono rispettare rigorosi standard di integrità dei dati (ad esempio, i dati devono essere completi e corretti) e di coerenza dei dati (ad esempio, il valore deve essere lo stesso in tutte le tabelle/database). Questo è spesso il caso in cui è coinvolta la responsabilità fiduciaria o la conformità normativa.
Possiamo annoverare anche transazioni di banche commerciali, intermediazione di investimenti e accordi legali.
In queste situazioni, un'aderenza standard alle definizioni DBMS non è sufficiente e sono richieste transazioni ACID.
Operazioni ACID
ACID è un acronimo che sta per atomicità, coerenza, isolamento e durabilità. Insieme, le proprietà ACID assicurano che un insieme di operazioni del database (raggruppate in una transazione) lascino il database in uno stato valido anche in caso di errori imprevisti.
Inoltre, le transazioni ACID forniscono il livello di garanzie transazionali che molti enti di regolamentazione richiedono.
Di seguito è riportata una panoramica generale di ogni elemento di una transazione ACID, nonché una descrizione di come un database di documenti NoSQL è in grado di gestire tale elemento ACID. Ai fini di questo articolo, sarà utilizzato MongoDB Atlas.
Atomicità
L'atomicità garantisce che tutti i comandi che compongono una transazione siano trattati come un'unica unità e che siano completati, con successo o meno, nel loro insieme. Questo è importante in caso di guasto del sistema o di interruzione dell'alimentazione, in quanto se una transazione non è stata elaborata completamente, sarà scartata e il database manterrà l'integrità dei dati.
In che modo MongoDB gestisce l'atomicità
In MongoDB, un'operazione di scrittura è atomica a livello di un singolo documento, anche se l'operazione modifica più documenti incorporati all'interno di un singolo documento.
Per le situazioni che richiedono l'atomicità delle operazioni di lettura e scrittura su più documenti (in una singola tabella o in più tabelle), MongoDB supporta transazioni distribuite, comprese le transazioni su replica set e sharded cluster.
Coerenza
La coerenza garantisce che le modifiche apportate all'interno di una transazione siano propagate in tutto il sistema di database (ad esempio, i nodi) e rispettino i vincoli del DBMS. Se la coerenza dei dati rischia di essere compromessa a causa di una transazione in uno stato incoerente, l'intera transazione verrà annullata.
Come MongoDB gestisce la coerenza
MongoDB offre la flessibilità di normalizzare o duplicare i dati per ottimizzare le applicazioni. Se i dati sono duplicati nello schema, lo sviluppatore deve decidere come mantenere coerenti i dati duplicati in più tabelle. Alcune applicazioni richiedono che i dati duplicati siano resi coerenti immediatamente, mentre altre applicazioni possono tollerare la lettura di dati non aggiornati. Di seguito sono illustrati alcuni esempi:
Nota: Scopri di più sulla coerenza dei dati.
Isolamento
Ogni transazione è isolata dalle altre transazioni per evitare conflitti di dati. Ciò semplifica anche le operazioni del database in relazione alla gestione di più voci e transazioni multilivello.
Ad esempio, se due utenti stanno cercando di modificare gli stessi dati (o anche la stessa transazione), il DBMS utilizza un meccanismo detto lock manager per sospendere gli altri utenti fino al completamento delle modifiche apportate dal primo utente.
Come MongoDB gestisce l'isolamento
MongoDB utilizza una tecnica detta snapshot isolation (ad esempio, ogni transazione sembra operare su uno snapshot personale del database creato all'inizio della transazione). Le transazioni possono leggere i dati dallo "snapshot" dei dati confermati al momento dell'inizio della transazione e qualsiasi aggiornamento in conflitto causerà l'annullamento della transazione.
Inoltre, le transazioni MongoDB supportano una modalità di scrittura a livello di transazione. I client possono impostare un livello appropriato di controllo per le operazioni di lettura e scrittura, con il livello più rigoroso che combina lo snapshot read concern con il majority write concern. Il majority write concern significa che le operazioni di scrittura sono state definitivamente confermate nella maggioranza calcolata dei nodi che contengono i dati (configurabile dallo sviluppatore).
Durabilità
La durabilità garantisce che, una volta completata la transazione e scritte le modifiche nel database, queste vengano mantenute. Questo assicura che i dati all'interno del sistema rimangano salvati anche in caso di guasti al sistema, come arresti improvvisi o interruzioni di corrente. Il concetto di durabilità è un elemento chiave dell'affidabilità dei dati.
Come MongoDB gestisce la durabilità
MongoDB crea un OpLog contenente la posizione del disco e i byte modificati per ogni "scrittura". Se si verifica un evento imprevisto (ad esempio un'interruzione di corrente) durante la scrittura della transazione, l'OpLog può essere utilizzato al riavvio del sistema per ripetere le scritture che non sono state salvate su disco prima dello spegnimento.
Inoltre, le operazioni vengono modificate prima di essere scritte nell'OpLog in modo che siano idempotenti e possano essere ripetute più volte. Per impostazione predefinita, le transazioni, o "scritture", vengono salvate su disco ogni 60 secondi circa.
Perché le transazioni ACID sono importanti?
Le transazioni ACID aiutano a mantenere l'integrità e l'affidabilità dei dati, garantendo al contempo che i dati vitali soggetti a normative governative o di settore (ad esempio, conto bancario, portafoglio azionario) soddisfino gli standard richiesti. Inoltre, la conformità all'ACID è spesso un prerequisito per implementare la replica dei dati e ottenere un'elevata disponibilità nei sistemi di database distribuiti.
Esempio di ACID transaction nei DBMS
Utilizzando il database di documenti NoSQL MongoDB Atlas, ecco un esempio di come vengono gestite le transazioni ACID con più documenti e di come le transazioni ACID garantiscono l'allineamento agli standard minimi delle proprietà ACID.
Transazioni ACID multi-documento
Immagina di creare una funzione per trasferire denaro da un conto bancario a un altro, in cui ogni conto rappresenta un record a sé stante. Se il denaro viene prelevato con successo dal conto di origine ma non viene mai accreditato sul conto di destinazione, si è creato un grave problema contabile. Al contrario, se il conto di destinazione viene accreditato ma il conto di origine non viene mai addebitato, si verifica un altro grave problema contabile.
Queste due operazioni di scrittura devono avvenire o non avvenire entrambe per mantenere il sistema e i suoi dati coerenti. Inoltre, ciò significa che se un comando della transazione fallisce, il database deve eseguire il rollback (ad esempio, annullare) di tutte le modifiche scritte nel corso della transazione.
Nota: per ulteriori informazioni, visitare il repository Node.js Quick Start su GitHub per ottenere una copia dell'esempio di codice completo ed eseguirlo autonomamente.
Impatti da ricordare
Quando si gestiscono transazioni multi-documento in un sistema distribuito, è importante ricordare che esiste un impatto dovuto al sovraccarico delle prestazioni, che potrebbe incidere sui vincoli delle risorse e sugli obiettivi di prestazioni. Inoltre, poiché il database deve "bloccare" le risorse coinvolte per evitare che le scritture simultanee interferiscano tra loro (ad esempio, in caso di fallimento della transazione), altri client che tentano di scrivere dati potrebbero rimanere bloccati in attesa del completamento della transazione, con conseguenze sulla latenza dell'applicazione e sull'esperienza utente.
Come funzionano le transazioni ACID in MongoDB?
Il document model di MongoDB consente di archiviare i dati correlati in un unico documento. Il modello di documento, combinato con gli aggiornamenti atomici dei documenti, elimina la necessità di transazioni nella maggior parte dei casi d'uso. Tuttavia, ci sono casi in cui le transazioni MongoDB multi-documento e multi-collection rappresentano la scelta migliore.
Le transazioni MongoDB funzionano in modo simile alle transazioni in altri database. Per utilizzare una transazione, avvia una sessione MongoDB tramite un driver. Quindi, utilizza quella sessione per eseguire il tuo gruppo di operazioni sul database. È possibile eseguire qualsiasi operazione CRUD (creazione, lettura, aggiornamento ed eliminazione) su più documenti, collection e shard.
Per esempi di codice specifici su come implementare le transazioni, visualizza le seguenti guide rapide:
- Transazioni ACID multi-documento in MongoDB con Golang
- Transazioni ACID multi-documento in MongoDB con Java
- Transazioni ACID multi-documento in MongoDB con Node.js
- Transazioni ACID multi-documento in MongoDB con Python
Visita la documentazione dei driver MongoDB per le guide specifiche per ciascuno dei linguaggi ufficialmente supportati da MongoDB. Puoi anche visualizzare un elenco delle best practice e delle considerazioni sulla produzione per le transazioni.
Quando si devono utilizzare le transazioni multi-documento di MongoDB?
Le applicazioni che richiedono transazioni solitamente presentano casi d'uso in cui i valori vengono scambiati tra diverse parti. Si tratta in genere di applicazioni "System of Record " o "Line of Business ".
Tra gli esempi di applicazioni che possono trarre vantaggio dalle transazioni multi-documento si annoverano:
- Sistemi che trasferiscono fondi da un conto all'altro (ad esempio, applicazioni bancarie, sistemi di elaborazione dei pagamenti, piattaforme di trading).
- Sistemi di prenotazione e di supply chain in cui la proprietà di beni e servizi viene trasferita da una parte all'altra.
- Sistemi di fatturazione che archiviano le informazioni in record dettagliati e in record di riepilogo.
Quali sono le best practice per le transazioni in MongoDB?
In generale, si consiglia di modellare i dati in modo che i dati a cui si accede insieme siano archiviati insieme. Quando i dati vengono modellati in questo modo, non solo si ottengono prestazioni migliori, ma si elimina anche la necessità delle transazioni.
Per le applicazioni che richiedono transazioni, le best practice a cui attenersi sono le seguenti:
- Suddividere le transazioni di lunga durata in parti più piccole in modo che non superino il timeout predefinito di 60 secondi. (Da notare che questo timeout può essere esteso.) Assicurarsi che le operazioni nelle transazioni utilizzino indici in modo da essere eseguite rapidamente.
- Limitare ogni transazione a 1.000 modifiche ai documenti.
- Assicurati che siano configurati i read and write concern appropriati (tieni presente che a partire dalla versione 5.0, MongoDB utilizza per impostazione predefinita il majority write concern necessario).
- Aggiungere una gestione appropriata degli errori e riprovare a effettuare le transazioni che falliscono a causa di errori temporanei.
- Ricorda il costo in termini di prestazioni delle transazioni che coinvolgono più shard. Per maggiori informazioni su queste best practice, consulta il white paper Transazioni ACID multi-documento su MongoDB e la documentazione MongoDB sulle transazioni.
