Database relazionali vs NoSQL: come scegliere la base giusta per il proprio sistema
La scelta tra un database relazionale e uno NoSQL è una delle decisioni architetturali più importanti che dovrai prendere. Se la prendi nel modo giusto, il tuo sistema sarà in grado di scalare senza problemi; se invece sbagli, ti ritroverai a lottare con il tuo database per anni. Ecco cosa devi sapere davvero.
Ogni database memorizza dati. Ma le somiglianze finiscono qui. I database relazionali e quelli NoSQL rappresentano filosofie fondamentalmente diverse su come i dati debbano essere strutturati, memorizzati e interrogati — e tali differenze si ripercuotono su ogni livello dei sistemi costruiti su di essi.
Non si tratta di una contrapposizione tra vecchio e nuovo, né tra semplice e sofisticato. Entrambe le categorie comprendono sistemi maturi e collaudati, utilizzati su scala straordinaria. La questione riguarda l’idoneità allo scopo: quale modello si adatta alla struttura dei vostri dati, ai modelli di accesso della vostra applicazione e alle garanzie di coerenza richieste dai vostri utenti.
Che cos’è effettivamente un database relazionale
Un database relazionale organizza i dati in tabelle — righe e colonne con relazioni esplicitamente definite tra loro. Il termine “relazionale” nel nome non si riferisce al fatto che le tabelle siano correlate tra loro (sebbene lo siano), ma al concetto matematico di relazione: un insieme di tuple che condividono gli stessi attributi. Questo fondamento teorico, sviluppato da Edgar Codd presso IBM nel 1970, si è dimostrato straordinariamente duraturo.
Le caratteristiche fondamentali di un database relazionale sono:
Uno schema fisso. Prima di inserire i dati, si definisce la struttura: quali tabelle esistono, quali colonne ha ciascuna tabella, quale tipo di dati accetta ogni colonna e quali vincoli si applicano (NOT NULL, UNIQUE, CHECK e così via). Lo schema è il contratto tra l’applicazione e il database.
Normalizzazione. I dati sono organizzati in modo da eliminare le ridondanze. Il nome di un cliente compare una sola volta, nella tabella dei clienti. Ogni ordine fa riferimento a quel cliente tramite l’ID. Se il nome cambia, viene modificato in un unico punto. Non si tratta solo di ordine: è una garanzia di coerenza.
Transazioni ACID. Atomicità, Coerenza, Isolamento e Durabilità. Una transazione viene completata integralmente o non viene completata affatto. Se si trasferiscono fondi da un conto a un altro, l’addebito e l’accredito avvengono contemporaneamente oppure non avvengono affatto. Il database garantisce questo comportamento a livello di motore.
SQL. Il Structured Query Language (Linguaggio di interrogazione strutturato) è l’interfaccia universale per i database relazionali. È dichiarativo — descrivi ciò che vuoi, non come ottenerlo — ed è straordinariamente espressivo. Join, aggregazioni, funzioni di finestra, sottoquery, CTE: l’intera gamma di funzionalità di SQL offre gli strumenti per porre domande complesse sui propri dati senza dover scrivere codice applicativo per eseguire il lavoro.
I principali database relazionali — PostgreSQL, MySQL, SQL Server, Oracle — condividono queste proprietà pur differendo per le loro specifiche funzionalità, caratteristiche prestazionali e modelli di licenza. PostgreSQL, in particolare, è diventato la scelta predefinita per i nuovi progetti, combinando una rigorosa conformità agli standard con un set di funzionalità eccezionale.
Che cos’è effettivamente un database NoSQL
"NoSQL" è un termine generico che copre diverse categorie distinte di database, accomunate principalmente da ciò che non sono: non sono relazionali e non utilizzano SQL come interfaccia primaria (sebbene alcuni supportino linguaggi di query simili a SQL). Il termine va inteso come "non solo SQL" — un riconoscimento del fatto che il modello relazionale non è l’unico approccio valido.
Le principali categorie NoSQL sono:
I database a documenti memorizzano i dati sotto forma di documenti — in genere JSON o BSON — in cui ogni documento può avere una struttura diversa. MongoDB è il più diffuso; altri esempi includono CouchDB e Firestore. Non ci sono tabelle né schemi fissi; una “collezione” di documenti può contenere record con campi completamente diversi.
Gli archivi chiave-valore rappresentano il modello più semplice: ogni record è un valore indirizzabile tramite una chiave univoca. Redis ne è l’esempio principale, ampiamente utilizzato per la memorizzazione nella cache, l’archiviazione delle sessioni e le classifiche in tempo reale. Anche DynamoDB opera principalmente come archivio chiave-valore, con funzionalità relative ai documenti integrate a livello superiore.
I database a famiglie di colonne organizzano i dati per colonna anziché per riga, il che consente letture estremamente efficienti di colonne specifiche tra milioni di righe. Apache Cassandra e HBase ne sono gli esempi principali. Questo modello è progettato per carichi di lavoro con un elevato numero di scritture e volumi elevati, in cui i modelli di query sono noti in anticipo.
I database grafici modellano i dati come nodi e archi — entità e le relazioni tra di esse. Neo4j ne è l’esempio principale. Quando le relazioni tra le entità sono l’oggetto principale delle query — social network, motori di raccomandazione, rilevamento delle frodi — i database grafici possono esprimere tali query in modo naturale, laddove l’SQL richiederebbe una ricorsività profonda.
I database a serie temporali sono ottimizzati per i dati indicizzati in base al tempo: InfluxDB, TimescaleDB, Prometheus. Letture dei sensori, tick finanziari, metriche delle applicazioni — dati in cui il timestamp è sempre la chiave primaria e le query per intervallo temporale rappresentano il modello di accesso dominante.
La differenza strutturale che determina tutto il resto
La differenza più profonda tra i database relazionali e quelli NoSQL non sta nella sintassi o nella scalabilità, bensì nel rapporto tra struttura dei dati e flessibilità delle query.
I database relazionali offrono la massima flessibilità nelle query in cambio di una disciplina iniziale nello schema. Poiché i dati sono normalizzati e tipizzati, è possibile porre loro quasi qualsiasi domanda al momento della query. È possibile unire le tabelle in modi che non si sarebbero mai previsti al momento della progettazione dello schema. È possibile eseguire aggregazioni su dimensioni aggiunte mesi dopo la progettazione iniziale. Lo schema vincola i dati, ma il linguaggio di query offre libertà.
I database NoSQL propongono un compromesso opposto. Offrono flessibilità nel modo in cui si strutturano e si scrivono i dati, in cambio di modelli di query più vincolati. Un database documentale consente di memorizzare dati di qualsiasi forma senza necessità di migrazione. Tuttavia, eseguire query sui documenti in modo efficiente richiede di conoscere in anticipo i propri modelli di accesso e di progettare la struttura dei dati in base a essi.
Questa è la tensione fondamentale: flessibilità dello schema contro flessibilità delle query. È possibile avere entrambe, ma solo in una certa misura. Il modello di database scelto riflette quale compromesso sia più accettabile per il proprio caso d’uso.
Il teorema CAP e perché è importante
I sistemi distribuiti non possono garantire contemporaneamente Coerenza, Disponibilità e Tolleranza alle partizioni: possono raggiungere al massimo due di queste tre caratteristiche. Questo è il teorema CAP, ed è il fondamento teorico per comprendere perché i database relazionali e NoSQL si comportano in modo diverso in condizioni di guasto.
I database relazionali tradizionalmente danno priorità alla coerenza e alla tolleranza alle partizioni. Ogni lettura riflette la scrittura più recente. Se si verifica una partizione di rete e il database non può garantire la coerenza, rifiuterà di fornire dati non aggiornati: diventerà indisponibile piuttosto che fornire dati errati.
Molti database NoSQL — in particolare quelli progettati per la distribuzione orizzontale come Cassandra e DynamoDB — danno priorità alla disponibilità e alla tolleranza alle partizioni. Forniranno dati anche durante una partizione di rete, accettando che alcune operazioni di lettura possano restituire valori non aggiornati. Si tratta della “coerenza finale”: tutti i nodi convergeranno sullo stesso valore alla fine, ma esiste un intervallo di tempo durante il quale potrebbero non essere allineati.
L’implicazione pratica: se la vostra applicazione non può tollerare in alcun caso la lettura di dati non aggiornati — un saldo bancario, un conteggio delle scorte, una cartella clinica — avete bisogno di una forte coerenza. Se la vostra applicazione può tollerare una breve incoerenza in cambio di una disponibilità costante — un feed sui social media, un catalogo prodotti, un profilo utente — la coerenza finale è accettabile e vi offre libertà architettonica.
Esempi dal mondo reale: quando la scelta giusta ha fatto la differenza
GitHub: PostgreSQL al centro di una piattaforma globale
GitHub gestisce una delle più grandi piattaforme di collaborazione software al mondo su PostgreSQL: milioni di repository, miliardi di eventi, complesse query relazionali che coinvolgono utenti, organizzazioni, repository, pull request, issue e commenti. Il modello relazionale si adatta perfettamente al dominio: le relazioni tra queste entità costituiscono il prodotto stesso. Una pull request appartiene a un repository, è creata da un utente, viene revisionata da altri utenti, chiude issue e attiva pipeline di CI.
Per anni, GitHub ha gestito una singola istanza primaria di PostgreSQL con repliche di lettura. Man mano che la scala cresceva, hanno introdotto Vitess (un livello di sharding MySQL per alcuni carichi di lavoro) e ProxySQL per la gestione delle connessioni — ma il nucleo relazionale è rimasto. La lezione non è che PostgreSQL sia scalabile all’infinito senza sforzo. È che il modello relazionale era la scelta giusta per un dominio definito dalle relazioni, e lo sforzo investito nel suo scalamento è valso la pena proprio perché la migrazione verso un modello di dati diverso sarebbe stata più costosa.
Twitter: la dolorosa migrazione da MySQL a un’architettura mista
Twitter ha iniziato con MySQL e ha affrontato una crisi di scalabilità ben documentata quando la crescita degli utenti ha superato la capacità di un singolo database relazionale. Il problema principale era il grafico sociale — le relazioni follower/seguiti tra centinaia di milioni di utenti — e il problema del fanout della timeline: quando un utente con 10 milioni di follower pubblica un tweet, quel tweet deve apparire in 10 milioni di timeline quasi istantaneamente.
Il modello relazionale, così come era stato implementato da Twitter, non era in grado di gestire questo fanout in scrittura alla velocità richiesta. Twitter ha migrato il grafico sociale verso un archivio in memoria personalizzato e alla fine ha adottato Manhattan (il proprio archivio chiave-valore distribuito interno) per le timeline e Cassandra per altri carichi di lavoro con un elevato volume di scritture.
La lezione da trarre è sfaccettata. I dati fondamentali di Twitter — tweet, utenti, relazioni — sono fondamentalmente relazionali. Il problema non era il modello di dati; era il volume di scrittura e il fanout su una scala che richiedeva la distribuzione. La migrazione è stata costosa, tecnicamente complessa e ha richiesto anni di investimenti ingegneristici. Alle dimensioni di Twitter, è stata la scelta giusta. Alle dimensioni di quasi qualsiasi altra applicazione, sarebbe stata prematura.
MongoDB: un monito sulla flessibilità dello schema
All’inizio degli anni 2010, mentre MongoDB cresceva rapidamente in popolarità, molti team lo adottarono principalmente per la sua flessibilità dello schema — la capacità di iniziare a memorizzare dati senza definire prima uno schema. Per la prototipazione e lo sviluppo iniziale, ciò accelerò davvero il processo di iterazione.
I problemi sono emersi man mano che i sistemi maturavano. Senza l’applicazione dello schema, le incongruenze nei dati si accumulavano gradualmente. Campi che avrebbero dovuto essere obbligatori a volte erano assenti. Valori che avrebbero dovuto essere interi a volte erano stringhe. Le query che presupponevano una struttura coerente restituivano risultati inaspettati. I team si sono ritrovati a scrivere estese validazioni a livello di applicazione che il database avrebbe applicato automaticamente.
Questo non è un fallimento di MongoDB come database: è uno strumento appropriato per molti casi d’uso. È un fallimento del presupposto secondo cui la flessibilità dello schema sia incondizionatamente positiva. La flessibilità ha rinviato il costo di riflettere sulla struttura dei dati; non l’ha eliminato. I team che hanno utilizzato MongoDB senza disciplina si sono ritrovati a implementare una versione approssimativa di uno schema relazionale nel codice dell’applicazione, senza le garanzie che un database relazionale avrebbe fornito.
Amazon DynamoDB: una storia di successo per il caso d’uso giusto
Il carrello della spesa di Amazon è uno degli esempi canonici di NoSQL utilizzato correttamente. I requisiti erano chiari: il carrello della spesa doveva essere sempre disponibile (un cliente doveva poter aggiungere articoli in qualsiasi momento, anche durante un guasto regionale), doveva gestire un volume massiccio di scritture (centinaia di milioni di utenti che aggiungevano e rimuovevano articoli) e il modello di accesso era semplice e noto in anticipo (accesso sempre tramite ID cliente).
Il team di ingegneri di Amazon, nel documento del 2007 che ha ispirato DynamoDB, ha descritto la scelta deliberata della coerenza finale (eventual consistency) per il carrello: se due dispositivi aggiungessero articoli contemporaneamente durante una partizione di rete, entrambe le aggiunte sarebbero state conservate una volta risolta la partizione, anche se ciò significasse mostrare brevemente uno stato leggermente incoerente. Un carrello della spesa su cui è sempre possibile scrivere — anche a costo di una breve incoerenza — è più prezioso di un carrello che a volte risulta non disponibile.
Il design di DynamoDB incentrato su questo caso d’uso — semplice accesso chiave-valore, modelli di query noti, disponibilità prioritaria rispetto alla coerenza — è ciò che lo rende eccellente. Le applicazioni che cercano di utilizzare DynamoDB come database relazionale generico, eseguendo query complesse su più entità, incontrano regolarmente difficoltà.
Airbnb: Elasticsearch per la ricerca, PostgreSQL per i dati di riferimento
La ricerca su Airbnb — che filtra gli annunci in base a località, date, prezzo, servizi e disponibilità — è una query che nessun database relazionale è in grado di gestire in modo efficiente su larga scala. La combinazione di ricerca geografica, corrispondenza full-text e filtraggio complesso su più attributi su milioni di annunci è proprio il caso d’uso per cui Elasticsearch è stato progettato.
Airbnb utilizza Elasticsearch come livello di ricerca, mantenendo PostgreSQL come sistema di riferimento. Quando un annuncio viene aggiornato, la modifica viene scritta su PostgreSQL (la fonte di verità, con garanzie ACID complete) e poi propagata in modo asincrono a Elasticsearch (l’indice di ricerca, ottimizzato per gli specifici modelli di query dell’esperienza di ricerca).
Si tratta di un modello architettonico consolidato: utilizzare il database più adatto a ciascuna esigenza, mantenere una chiara fonte di verità e accettare la complessità operativa derivante dal mantenere sincronizzati più archivi. Funziona perché i confini sono chiari e i compromessi sono ben compresi.

Quando scegliere un database relazionale
Un database relazionale è la scelta predefinita più indicata per la maggior parte delle applicazioni. I casi in cui rappresenta chiaramente la scelta giusta:
I vostri dati presentano relazioni significative. Se le entità nel vostro sistema sono correlate tra loro in modi rilevanti — utenti e i loro post, ordini e le relative voci, progetti e le relative attività — il modello relazionale rappresenta direttamente tali relazioni e vi consente di eseguire interrogazioni su di esse in modo efficiente.
Hai bisogno di una forte coerenza. Transazioni finanziarie, gestione delle scorte, sistemi di prenotazione, cartelle cliniche: qualsiasi ambito in cui una lettura errata comporti conseguenze concrete richiede garanzie ACID. Un database relazionale fornisce tutto ciò a livello di motore.
I vostri modelli di query non sono del tutto noti. Se la vostra applicazione dovrà rispondere a domande sui dati che non potete prevedere appieno in fase di progettazione, la flessibilità di SQL è preziosa. La capacità di scrivere una query ad hoc su uno schema ben normalizzato è una risorsa operativa significativa.
Il vostro team conosce SQL. Può sembrare banale, ma non lo è. L’SQL è una delle competenze tecniche più diffuse nell’ingegneria del software. Ogni sviluppatore che assumerete lo conoscerà in una certa misura. Le conoscenze operative — come aggiungere un indice, come interpretare un piano di query, come gestire i backup — sono ampiamente disponibili. L’ecosistema degli strumenti è maturo.
Non siete sicuri di cosa vi serva. In caso di dubbio, iniziate con PostgreSQL. È più facile introdurre un database specializzato in un secondo momento, una volta compresi i vostri effettivi modelli di accesso, piuttosto che migrare da un database documentale a un modello relazionale quando i vostri dati si rivelano più strutturati di quanto pensaste.
Quando scegliere NoSQL
NoSQL è la scelta giusta quando un requisito specifico non può essere soddisfatto dal modello relazionale senza uno sforzo sproporzionato:
Il volume di scrittura supera ciò che un singolo nodo è in grado di gestire. Se la vostra applicazione deve sostenere centinaia di migliaia di operazioni di scrittura al secondo su un sistema distribuito geograficamente, diventa necessario il ridimensionamento orizzontale su molti nodi. I database a famiglie di colonne come Cassandra sono progettati proprio per questo; i database relazionali possono essere distribuiti, ma non è la loro modalità naturale.
I vostri dati sono realmente privi di schema o altamente variabili. Un catalogo prodotti in cui tipi di prodotti diversi presentano attributi completamente diversi, un CMS in cui i tipi di contenuto si evolvono rapidamente, un sistema di logging in cui gli schemi degli eventi sono definiti da sistemi esterni: questi casi traggono vantaggio dall’archiviazione di documenti, in cui la struttura non è imposta dal database.
Il vostro modello di accesso principale è la ricerca chiave-valore su scala estrema. Archivi di sessioni, cache, classifiche in tempo reale, flag di funzionalità: i carichi di lavoro in cui si accede sempre ai dati tramite una singola chiave e non è mai necessario eseguire join o aggregazioni traggono vantaggio dalla semplicità e dalle prestazioni di un archivio chiave-valore.
State modellando un grafo. Se le vostre query principali riguardano l’esplorazione delle relazioni — trovare tutti gli amici degli amici, identificare il percorso più breve tra due nodi, rilevare dipendenze circolari — un database di grafi esprimerà tali query in modo più naturale e le eseguirà in modo più efficiente rispetto all’SQL ricorsivo.
Hai bisogno di ottimizzazione per le serie temporali. Milioni di letture da sensori, tick finanziari o metriche delle applicazioni al secondo, con query che riguardano principalmente intervalli di tempo, trovano una soluzione migliore in un database per serie temporali appositamente progettato piuttosto che in un database relazionale con una colonna di timestamp.
La realtà dei database multipli
La cosa più importante da comprendere riguardo alla questione “relazionale vs NoSQL” è che, nella maggior parte dei sistemi maturi, non si tratta di una scelta “o l’uno o l’altro”. I sistemi su larga scala utilizzano abitualmente più database, ciascuno scelto per il carico di lavoro specifico che deve gestire.
PostgreSQL per il sistema di registrazione. Redis per la memorizzazione nella cache delle sessioni e la limitazione della frequenza. Elasticsearch per la ricerca full-text. InfluxDB per le metriche delle applicazioni. Ogni database in questo stack svolge ciò che sa fare meglio, e il livello applicativo gestisce la coerenza tra di essi.
Il costo operativo di questa complessità è reale. Ogni database aggiuntivo rappresenta un altro sistema da monitorare, di cui eseguire il backup, da scalare e da comprendere. La disciplina ingegneristica necessaria per mantenere la coerenza tra più archivi — gestendo scenari di errore in cui una scrittura su PostgreSQL va a buon fine ma il successivo aggiornamento su Elasticsearch fallisce — non è affatto banale.
Il punto di partenza per quasi tutte le applicazioni dovrebbe essere un unico database relazionale. Aggiungete archivi specializzati solo quando un requisito specifico e ben compreso non può essere soddisfatto senza di essi. La diversificazione prematura dei database è una forma di ottimizzazione prematura: aggiunge complessità prima ancora di capire se tale complessità sia necessaria.

Prendere una decisione
Le domande che contano davvero quando si sceglie un database:
Quali sono i vostri requisiti di coerenza? Se la risposta alla domanda "cosa succede se un utente legge dati non aggiornati" è "è un problema grave", avete bisogno di ACID. Se la risposta è "è leggermente fastidioso ma accettabile", avete diverse opzioni.
Quali sono i tuoi modelli di query? Se sai esattamente come verranno consultati i tuoi dati e quel modello di accesso è semplice e ad alto volume, puoi ottimizzare il sistema di conseguenza. Se i tuoi modelli di accesso sono complessi o non del tutto noti, la flessibilità di SQL è preziosa.
Qual è il tuo volume di scrittura? Per la stragrande maggioranza delle applicazioni, un’istanza di PostgreSQL ben ottimizzata su hardware adeguato gestisce senza difficoltà migliaia di operazioni di scrittura al secondo. I casi in cui ciò risulta davvero insufficiente sono più rari di quanto suggerisse il clamore suscitato dal NoSQL negli anni 2010.
Qual è la struttura dei vostri dati? I dati altamente uniformi e fortemente correlati si adattano bene al modello relazionale. I dati altamente variabili e debolmente correlati potrebbero adattarsi meglio a un modello documentale.
Quali sono le competenze del vostro team? La familiarità operativa è fondamentale. Un team che conosce a fondo PostgreSQL realizzerà un sistema più affidabile su PostgreSQL piuttosto che su un database NoSQL che sta ancora imparando a utilizzare, anche se quest’ultimo fosse teoricamente più adatto.
Il database relazionale è sopravvissuto a cinquant’anni di cambiamenti tecnologici non perché sia sempre lo strumento migliore, ma perché è un’ottima scelta predefinita: flessibile, affidabile, ben compreso e supportato da decenni di esperienza operativa. Allontanatevi da esso in modo consapevole, con una chiara comprensione di ciò che guadagnate e di ciò a cui rinunciate.