I sistemi di database hanno il mandato di garantire la coerenza e l'integrità dei dati, specialmente quando sono coinvolti dati critici. Questi aspetti sono applicati attraverso le transazioni ACID in MongoDB. Una transazione ACID dovrebbe soddisfare alcune regole definite per la validità dei dati prima di apportare qualsiasi aggiornamento al database, altrimenti dovrebbe essere interrotta e non verranno apportate modifiche al database. Tutte le transazioni del database sono considerate come un'unica operazione logica e durante il tempo di esecuzione il database viene posto in uno stato incoerente fino al completamento delle modifiche. Le operazioni che modificano correttamente lo stato del database sono denominate transazioni di scrittura, mentre quelle che non aggiornano il database ma recuperano solo i dati vengono definite transazioni di sola lettura. ACID è l'acronimo di Atomicity, Consistency, Isolation e Durability.
Un database è una risorsa condivisa a cui possono accedere utenti diversi in momenti diversi o contemporaneamente. Per questo motivo possono verificarsi transazioni simultanee e, se non ben gestite, possono causare arresti anomali del sistema, guasti hardware, deadlock, prestazioni del database lente o ripetizioni nell'esecuzione della stessa transazione.
Cosa sono le regole ACID?
Tutti i sistemi di database devono soddisfare le proprietà ACID per garantire l'integrità dei dati.
Atomicità
Una transazione è considerata come una singola unità operativa che può avere esito positivo o negativo. Una transazione non può essere eseguita parzialmente. Se una qualsiasi condizione di consultazione di una transazione fallisce, l'intera transazione fallirà completamente e il database rimarrà invariato. Ad esempio, se vuoi trasferire fondi dal conto X a Y, qui ci sono due transazioni, la prima consiste nel rimuovere fondi da X e la seconda è registrare i fondi in Y. Se la prima transazione fallisce, l'intero la transazione verrà interrotta
Coerenza
Quando viene emessa un'operazione, prima dell'esecuzione, il database è in uno stato coerente e dovrebbe rimanere tale dopo ogni transazione. Anche se c'è un aggiornamento, la transazione dovrebbe portare sempre il database in uno stato valido, mantenendo le invarianti del database. Ad esempio, non è possibile eliminare una chiave primaria a cui è stato fatto riferimento come chiave esterna in un'altra raccolta. Tutti i dati devono soddisfare i vincoli definiti per prevenire il danneggiamento dei dati da una transazione illegale.
Isolamento
Più transazioni eseguite contemporaneamente vengono eseguite senza influenzarsi a vicenda e il loro risultato dovrebbe essere lo stesso se dovessero essere eseguite in sequenza. Quando due o più transazioni modificano gli stessi documenti in MongoDB, potrebbe esserci un conflitto. Il database rileverà un conflitto immediatamente prima che venga eseguito il commit. La prima operazione per acquisire un lock sul documento continuerà mentre l'altra fallirà e verrà presentato un messaggio di errore di conflitto.
Durata
Ciò impone che, una volta che la transazione è stata confermata, le modifiche dovrebbero essere mantenute in qualsiasi momento anche in caso di guasto del sistema, ad esempio a causa di interruzioni di corrente o disconnessione di Internet.
Transazioni ACID MongoDB
MongoDB è un database NoSQL basato su documenti con uno schema flessibile. Le transazioni non sono operazioni che devono essere eseguite per ogni operazione di scrittura poiché comportano un costo di prestazioni maggiore rispetto alle scritture di un singolo documento. Con una struttura basata su documenti e un modello di dati denormalizzato, la necessità di transazioni sarà ridotta al minimo. Poiché MongoDB consente l'incorporamento di documenti, non è necessario utilizzare una transazione per eseguire un'operazione di scrittura.
MongoDB versione 4.0 fornisce supporto per transazioni multi-documento solo per distribuzioni di set di repliche e probabilmente la versione 4.2 estenderà il supporto per distribuzioni partizionate (secondo le note di rilascio).
Esempio di transazione:
Assicurati di avere prima una replica impostata. Supponendo che tu abbia un database chiamato app e una raccolta utenti in Mongo Shell, esegui i seguenti comandi:
$mongos e dovresti vedere qualcosa come username:PRIMARY>
$use app
$db.users.insert([{_id:1, name: ‘Brian’}, {_id:2, name: ‘Sheila’}, {_id:3, name: ‘James’}])
Dobbiamo avviare una sessione per la nostra transazione:
$db.getMongo().startSession() and you should see something like
session { "id" : UUID("dcfa8de5-627d-3b1c-a890-63c9a355520c") }
Utilizzando questa sessione possiamo aggiungere più utenti utilizzando una transazione con i seguenti comandi
$session.startTransaction()
session.getDatabase(‘app’).users.insert({_id:4, name: ‘Hitler’})
Ti verrà presentato WriteResult({“nInsterted”:2})
La transazione non è stata ancora confermata e il normale $db.users.find({}) ci darà solo gli utenti precedentemente salvati. Ma se eseguiamo il
$session.getDatabase(“app”).users.find()
l'ultimo record aggiunto sarà disponibile nei risultati restituiti. Per eseguire questa transazione, eseguiamo il comando seguente
$session.commitTransaction()
La modifica della transazione viene archiviata in memoria, ecco perché anche dopo un errore, i dati saranno disponibili al ripristino.
Transazioni ACID multi-documento in MongoDB
Queste sono operazioni con più istruzioni che devono essere eseguite in sequenza senza influenzarsi a vicenda. Per l'esempio sopra possiamo creare due transazioni, una per aggiungere un utente e un'altra per aggiornare un utente con un campo di età. Cioè
$session.startTransaction()
db.users.insert({_id:6, name “Ibrahim”})
db.users.updateOne({_id:3 , {$set:{age:50}}})
session.commit_transaction()
Le transazioni possono essere applicate alle operazioni su più documenti contenuti in una o più raccolte/database. Eventuali modifiche dovute alla transazione di documenti non influiscono sulle prestazioni per carichi di lavoro non correlati o non le richiedono. Fino al commit della transazione, le scritture senza commit non vengono replicate sui nodi secondari né sono leggibili al di fuori delle transazioni.
Best practice per le transazioni MongoDB
Le transazioni multi-documento sono supportate solo nel motore di archiviazione WiredTiger. Come accennato in precedenza, pochissime applicazioni richiederebbero transazioni e, in tal caso, dovremmo cercare di renderle brevi. In caso contrario, per una singola transazione ACID, se si tenta di eseguire un numero eccessivo di operazioni, può causare un'elevata pressione sulla cache di WiredTiger. La cache è sempre dettata per mantenere lo stato per tutte le scritture successive dalla creazione dello snapshot meno recente. Ciò significa che le nuove scritture si accumuleranno nella cache per tutta la durata della transazione e verranno cancellate solo dopo il commit o l'interruzione delle transazioni attualmente in esecuzione su vecchi snapshot. Per le migliori prestazioni del database sulla transazione, gli sviluppatori dovrebbero considerare:
- Modifica sempre un numero ridotto di documenti in una transazione. In caso contrario, dovrai suddividere la transazione in parti diverse ed elaborare i documenti in lotti diversi. Al massimo, elabora 1000 documenti alla volta.
- Eccezioni temporanee come l'attesa di eleggere singhiozzi di rete primari e transitori possono comportare l'interruzione della transazione. Gli sviluppatori dovrebbero stabilire una logica per riprovare la transazione se vengono presentati gli errori definiti.
- Configura la durata ottimale per l'esecuzione della transazione dai 60 secondi predefiniti forniti da MongoDB. Inoltre, utilizza l'indicizzazione in modo che possa consentire un rapido accesso ai dati all'interno della transazione. Hai anche la flessibilità di perfezionare la transazione nell'affrontare i timeout suddividendola in batch che ne consentono l'esecuzione entro i limiti di tempo.
- Scomponi la tua transazione in un piccolo insieme di operazioni in modo che soddisfi i vincoli di dimensione di 16 MB. In caso contrario, se l'operazione insieme alla descrizione dell'oplog supera questo limite, la transazione verrà interrotta.
- Tutti i dati relativi a un'entità dovrebbero essere archiviati in un'unica struttura ricca di documenti. Questo serve a ridurre il numero di documenti che devono essere memorizzati nella cache quando diversi campi verranno modificati.
Limitazioni delle transazioni
- Non puoi creare o eliminare una raccolta all'interno di una transazione.
- Le transazioni non possono effettuare scritture su una raccolta limitata
- Le transazioni richiedono molto tempo per essere eseguite e in qualche modo possono rallentare le prestazioni del database.
- La dimensione della transazione è limitata a 16 MB, richiedendo di suddividere quelle che tendono a superare questa dimensione in transazioni più piccole.
- L'assoggettamento di un numero elevato di documenti a una transazione può esercitare una pressione eccessiva sul motore WiredTiger e poiché si basa sulla capacità di snapshot, ci sarà una conservazione in memoria di grandi operazioni non scaricate. Ciò comporta un certo costo delle prestazioni del database.
Conclusione
MongoDB versione 4.0 ha introdotto il supporto per le transazioni multi-documento per i set di repliche come funzionalità per migliorare l'integrità e la coerenza dei dati. Tuttavia, ci sono pochissime applicazioni che richiedono transazioni quando si utilizza MongoDB. Ci sono limitazioni contro questa caratteristica che la rendono considerevolmente un po' immatura per quanto riguarda il concetto di transazione. Ad esempio, le transazioni per un cluster partizionato non sono supportate e non possono superare il limite di dimensioni di 16 MB. La modellazione dei dati fornisce una struttura migliore per ridurre le transazioni nel database. A meno che non si tratti di casi speciali, sarà una pratica migliore evitare le transazioni in MongoDB.