Guida pratica per scegliere un database in base ai percorsi di lettura/scrittura, latenza, consistenza e necessità di crescita—così le tendenze non creano debito tecnico evitabile.

Scegliere un database perché è “popolare” è come comprare un veicolo perché tutti ne parlano—senza verificare se ti serve uno scooter, un pickup o un autobus. Le tendenze riflettono ciò che ha funzionato per il prodotto, la dimensione del team, il budget e la tolleranza al rischio di qualcun altro. Il tuo database deve adattarsi al tuo carico di lavoro: a ciò che la tua applicazione fa davvero tutto il giorno.
Un carico di lavoro è il comportamento reale del sistema in produzione:
Questi comportamenti sono i tuoi pattern di accesso—i modi ripetibili in cui la tua app tocca i dati. Se riesci a descriverli chiaramente, la scelta del database diventa molto meno misteriosa.
Una soluzione unica raramente va bene per tutto. Molti sistemi di successo usano un approccio ibrido: un database ottimizzato per le transazioni, un altro per l'analisi e talvolta un motore di ricerca o una cache dedicata. Non è “complessità inutile”—è riconoscere che pattern diversi traggono vantaggio da storage e motori di query diversi.
Prima di confrontare “SQL vs NoSQL” o inseguire ciò che è di moda, scrivi le tue prime 5–10 letture e scritture più importanti. Parti da lì; il resto sono dettagli.
Un pattern di accesso è la descrizione pratica di come la tua applicazione tocca i dati giorno per giorno: cosa legge, cosa scrive, quanto spesso, quanto velocemente e in quali forme. Conta meno cosa sono i dati (“ordini” o “utenti”) e più cosa ci fai (“recuperare un ordine per ID 10.000 volte al minuto” o “scansionare tutti gli ordini del mese scorso per costruire un report”).
La maggior parte del traffico di lettura rientra in alcuni casi riconoscibili:
Un feed social è un buon esempio di forme miste: potresti fare point lookups per i profili, letture per intervallo per gli “ultimi post” e aggregazioni per i conteggi.
I pattern di scrittura contano altrettanto:
I log sono spesso “write-heavy e append-only” (molti insert, pochi update). Gli ordini sono tipicamente “write-then-update” (creazione, poi cambi di stato).
Molti prodotti vogliono tutto: lookup rapidi per l'app, query complesse per il supporto clienti e grandi scansioni per analytics. Un singolo database può gestire bene alcune combinazioni, ma certe combinazioni si ostacolano a vicenda—per esempio, scansioni analitiche pesanti possono rallentare le letture a bassa latenza che servono il checkout o un feed.
Quando sai descrivere chiaramente i tuoi pattern di accesso, puoi valutare i database sul comportamento reale invece che sulla popolarità.
Prima di confrontare marche di database, nomina il carico di lavoro che stai realmente servendo. La maggior parte dei prodotti non è “un solo workload”—sono pochi workload distinti affiancati (e a volte in competizione). Classificarli correttamente fin dall'inizio evita di forzare un database in un ruolo per cui non è ottimizzato.
L'OLTP è il battito quotidiano della maggior parte delle app: molte piccole letture e scritture, tanti utenti concorrenti e richieste che devono finire in fretta.
Pensa: “aggiorna un carrello”, “crea un ordine”, “modifica un indirizzo”, “controlla l'inventario.” Operazioni brevi, mirate e sensibili alla correttezza. Se un pagamento viene catturato, non deve sparire; se un posto è prenotato, non devono ottenerlo due persone.
L'OLTP tipicamente ti spinge verso sistemi che gestiscono bene l'alta concorrenza e offrono garanzie chiare su transazioni e integrità dei dati.
L'analytics cambia la forma del lavoro: meno query, ma ciascuna tocca molti più dati.
Pensa: “ricavi per regione dell'ultimo trimestre”, “conversione per canale”, “prodotti top per categoria”, “trend utenti attivi giornalieri.” Queste query spesso scansionano molte righe, raggruppano, aggregano e ordinano. Le aspettative di latenza possono essere più permissive (secondi possono andare bene), ma il costo delle scansioni pesanti conta—soprattutto se le dashboard girano tutto il giorno.
Se provi a eseguire scansioni OLAP sullo stesso sistema che gestisce il checkout, finirai spesso con uno dei due che soffre.
Time-series e log sono solitamente append-heavy: nuovi eventi arrivano costantemente e si interrogano soprattutto per intervalli temporali.
Pensa: metriche, clickstream, telemetria device, audit log. Necessità comuni includono politiche di retention (cancellare/scadere dati vecchi), rollup (tenere eventi raw per 7 giorni, aggregati per 12 mesi) e scritture rapide durante picchi.
Questo workload riguarda più l'ingestione efficiente di molti record con timestamp e il tenere lo spazio di archiviazione prevedibile nel tempo, meno i join complessi.
La ricerca non è solo “trovare righe.” È matching testuale, ranking per rilevanza, corrispondenze parziali e filtri user-friendly.
Pensa: cercare prodotti per parole chiave, trovare ticket per frasi, filtrare per faccette (marca, fascia prezzo, colore) e ordinare per “miglior corrispondenza.” Queste funzionalità spesso richiedono indicizzazione specializzata e capacità di query che i database generalisti possono approssimare—ma raramente padroneggiare.
Se la ricerca è una funzione centrale del prodotto, trattala come workload a sé fin dall'inizio, non come un dettaglio da aggiungere dopo.
La performance non è un solo numero. Due database possono essere entrambi “veloci”, ma dare sensazioni completamente diverse a utenti e operatori. Per scegliere bene, separa ciò che percepiscono gli utenti (latenza) da ciò che il sistema deve sostenere (throughput), poi metti alla prova le ipotesi con i picchi.
Latenza è quanto impiega una singola richiesta—“premi il pulsante, ottieni il risultato.” Gli utenti percepiscono la latenza direttamente.
Throughput è quante richieste puoi processare al secondo—quanto traffico totale il sistema può gestire.
Un database può raggiungere alto throughput accorpando operazioni, ma avere comunque un ritardo percepibile per ogni singola richiesta. Un altro può ottimizzare letture rapide per punti, ma soffrire quando arrivano molte scritture insieme.
La latenza media nasconde i dolori. Se 99 richieste finiscono in 50 ms e 1 impiega 2 secondi, la media sembra ok—ma quell'1% è il momento “l'app è lenta”.
Questo è il significato della latency P99: il tempo delle richieste più lente (1%). Per funzionalità rivolte all'utente (checkout, login, risultati di ricerca), il P99 spesso decide se il design del database sembra affidabile.
La maggior parte dei sistemi non fallisce al traffico medio; fallisce durante i picchi: una email marketing, un evento di breaking news, il payroll, fine mese.
I picchi cambiano la conversazione sul database:
La cache può far sembrare i workload di lettura più piccoli—finché non c'è un miss o una purge.
Se la maggior parte delle letture colpisce la cache, il tuo database potrebbe servire principalmente scritture e occasionali letture costose. Questo favorisce scelte diverse rispetto a un sistema in cui ogni lettura arriva al database. Pianifica per eventi di “cold cache” e per la latenza di coda dei miss, non solo per il percorso felice.
Scegliere un database non riguarda solo la velocità. Riguarda anche cosa può essere sbagliato, quanto downtime puoi tollerare e dove sono i tuoi utenti.
Inizia nominando i dati che devono essere corretti sempre. Pagamenti, saldi account e conteggi inventario sono esempi classici. Se un cliente viene addebitato due volte o vendi più stock di quanto disponibile, il costo non è solo un'app più lenta—sono rimborsi, ticket di supporto e perdita di fiducia.
Per queste parti del sistema, di solito vuoi garanzie forti: le scritture devono essere confermate prima di considerarle completate e i lettori non devono vedere aggiornamenti a metà. Il compromesso è che una correttezza più forte spesso riduce la flessibilità: alcune strategie di scaling diventano più difficili e le scritture cross-region possono essere più lente.
Poi, decidi cosa succede se il database non è disponibile per 5 minuti.
Se il downtime significa “si fermano gli ordini e si ferma il revenue”, hai bisogno di maggiore disponibilità: failover automatico, buoni backup e un piano per manutenzione senza spegnere l'app. Se il downtime significa “i dashboard interni sono in ritardo”, puoi accettare soluzioni più semplici.
Maggiore disponibilità tipicamente aumenta costi e complessità operativa (più repliche, più monitoraggio, upgrade più attenti). L'importante è allineare quell'investimento all'impatto sul business.
Se i tuoi utenti sono per lo più in una regione, mantenere i dati in un posto può essere più economico e veloce. Se hai utenti su più continenti—o requisiti normativi sulla localizzazione dei dati—potresti aver bisogno di replica multi-regione.
I design multi-regione migliorano esperienza utente e resilienza, ma impongono scelte difficili: permetti letture leggermente stale o accetti scritture più lente per mantenere tutto perfettamente sincronizzato? La risposta giusta dipende da cosa il tuo workload può tollerare.
La maggior parte dei “dibattiti sui database” sono in realtà discussioni sulla forma delle query. Se sai quali domande la tua app deve porre—join, aggregazioni, filtri, finestre temporali—puoi solitamente restringere le opzioni di database rapidamente.
Un modello relazionale brilla quando hai bisogno di filtri flessibili e join tra entità multiple (customers → orders → items), specialmente quando i requisiti evolvono. Se il prodotto richiede report ad-hoc (“mostrami tutti i clienti che hanno comprato X e anche restituito Y”), SQL e i join tendono a rimanere più semplici nel tempo.
Se le tue query sono prevedibili e per lo più basate sulla chiave primaria (“get profile by user_id”), un modello documentale o key-value può funzionare bene—spesso memorizzando i dati già organizzati nel modo in cui li leggi. Il compromesso è duplicare dati per evitare join, spostando la complessità nelle scritture e negli aggiornamenti.
Gli indici sono come dici al database “questi sono i miei pattern di accesso.” Una query che sembra ok in un mockup può diventare lenta se filtra o ordina su campi non indicizzati.
Una regola utile: ogni filtro, ordinamento o chiave di join frequente dovrebbe avere un piano di indice. Ma gli indici non sono gratis: consumano spazio e appesantiscono le scritture.
Le promesse di “scritture veloci” spesso ignorano l'amplificazione di scrittura—lavoro extra creato da indici secondari, compaction, replica o dall'aggiornamento di più copie di dati denormalizzati. Un design che ottimizza le letture aggiungendo indici o duplicando documenti può trasformare silenziosamente un workload ad alto write in un collo di bottiglia.
Schema-less non significa senza struttura. Schemi flessibili accelerano l'iterazione iniziale, ma senza convenzioni creano campi incoerenti, query difficili da debug e migrazioni costose dopo. Quando ti aspetti molti team, molte feature o lunghi periodi di retention, uno schema più definito e vincoli chiari spesso riducono il costo totale—anche se inizialmente sembrano rallentare.
Scegliere un database perché è popolare spesso si ritorce contro nelle parti poco glamour dell'ownership: tenerlo in funzione, tenerlo sicuro e pagare la fattura mese dopo mese. Due database possono soddisfare gli stessi requisiti funzionali, ma differire molto nello sforzo operativo e nel costo totale.
Chiedi presto chi lo gestirà alle 2 di notte. Backup, point-in-time recovery, upgrade, patching, drill di failover e monitoring non sono attività “dopo”—influiscono sul rischio e sul personale.
I servizi gestiti possono ridurre il toil, ma non lo eliminano. Alcuni sistemi richiedono compaction regolare, tuning accurato o competenze profonde per evitare rallentamenti. Altri rendono i cambi di schema penosi o richiedono playbook di migrazione speciali. Se il tuo team è piccolo, un database più facile da operare può battere un “fit perfetto” sulla carta.
I costi di un database di solito derivano da:
Un pattern di accesso ricco di scritture e indici secondari può moltiplicare I/O e storage anche quando il dataset è piccolo.
Linguaggi di query proprietari, funzionalità di consistenza uniche o “magie” serverless possono accelerare la delivery—ma limitare i movimenti futuri. Considera se puoi esportare i dati, eseguire localmente test o cambiare provider senza riscrivere l'app.
Al minimo, conferma cifratura in transito/at-rest, opzioni di gestione chiavi, auditing, controlli di accesso e politiche di retention. I requisiti di compliance spesso determinano la differenza tra “funziona” e “accettabile”, indipendentemente dalla trendiness.
Una volta descritti i pattern di accesso (cosa leggi, cosa scrivi, con quale frequenza e sotto quali picchi), la famiglia di database “giusta” diventa spesso più chiara. L'obiettivo non è scegliere lo strumento più popolare—ma il sistema più semplice che rimane corretto sotto il tuo carico.
Scegli un database relazionale quando hai bisogno di forte coerenza, relazioni chiare e transazioni affidabili—ordini, pagamenti, inventario, permessi, scheduling. Se interroghi frequentemente entità correlate (“clienti con fatture aperte negli ultimi 30 giorni”) o devi far rispettare vincoli (email uniche, foreign key), SQL tende a ridurre la complessità applicativa.
Una euristica comune: se il team sta per reimplementare join, vincoli e transazioni nel codice, probabilmente vuoi un relazionale.
Un database documentale funziona meglio quando leggi e scrivi per lo più oggetti completi che possono variare in struttura, come profili utente, pagine di contenuto, cataloghi prodotti con campi opzionali o impostazioni. Se la query tipica è “prendi il profilo per user_id” e aggiorni parti, i documenti tengono insieme i dati che usi.
Abbi cautela quando le query diventano altamente relazionali (molte query cross-document) o quando servono garanzie transazionali multi-entità.
I sistemi key-value eccellono per cache, sessioni, rate limit, feature flag e stato a breve vita dove il pattern è “get/set per chiave” e la latenza conta. Spesso sono un complemento, non il sistema di registrazione primario.
Se conservi dati business duraturi, chiediti cosa accade in caso di eviction, riavvio o ritardi di replica.
Per analytics—dashboard, retention cohort, rollup di ricavi, group-by su grandi storici—i sistemi columnar/warehouse vincono perché sono ottimizzati per scansionare e aggregare molte righe in modo efficiente.
Uno split pratico: tieni le scritture OLTP nel database primario e alimenta un warehouse per il reporting. Questo evita di rallentare le query lato cliente con workload BI.
Molti prodotti di successo non “scelgono un database.” Mappano ogni pattern di accesso allo storage più semplice che lo serve bene, anche se questo significa usare due o tre database affiancati.
Un negozio online ha spesso tre workload molto diversi:
Il prodotto sembra unificato, ma lo storage è specializzato per pattern di accesso.
Un tool B2B può memorizzare entità core (progetti, fatture, ticket) in un database transazionale, ma avere ancora bisogno di:
Piattaforme IoT ingestiscono burst di telemetria e poi le leggono per dashboard su finestre temporali.
Uno split comune è: uno store per ingestione veloce dei dati recenti, storage a lungo termine più economico per la retention e un motore analytics per gli aggregati.
Il punto chiave: componenti diversi possono e dovrebbero usare database diversi quando i loro pattern di accesso divergono.
Un mismatch di database si manifesta spesso come una pila crescente di “piccole” soluzioni tampone. Se il team passa più tempo a combattere il database che a costruire funzionalità, presta attenzione—spesso sono problemi di pattern di accesso, non di tuning.
Alcuni segnali ricorrenti:
Se il database richiede sforzi eroici per supportare operazioni normali, probabilmente workload e famiglia di database non coincidono.
Scegliere un database perché è di moda può portare a costi a lungo termine:
Il conto arriva quando la scala aumenta o i requisiti cambiano, e l'unica soluzione realistica è una re-platform dolorosa.
Non serve osservabilità perfetta, ma pochi segnali:
Annota i principali pattern di accesso (letture/scritture, query chiave, tassi di picco), le ipotesi sul volume dati e i “non negoziabili” (coerenza, disponibilità, vincoli di regione). Aggiungi riferimenti a dashboard ed esempi delle query peggiori. Quel breve documento accelera decisioni future—e rende chiaro quando un database non corrisponde più alla realtà.
Scegliere un database è più semplice se lo tratti come raccolta requisiti, non come gara di popolarità. Usa questa checklist per trasformare un vago “ci serve qualcosa scalabile” in input concreti comparabili.
Rispondi prima in linguaggio naturale, poi aggiungi numeri dove puoi:
Fai una tabella di una pagina con i criteri a sinistra e i candidati in cima. Marca ogni criterio come must-have o nice-to-have, poi valuta ciascun database (ad esempio 0–2).
Includi almeno: adeguatezza della query, approccio di scaling, esigenze di consistenza, sforzo operativo, ecosistema/tooling e prevedibilità dei costi.
Testa con dati rappresentativi e query reali, non esempi toy. Ricrea le “top query” e un pattern di scrittura realistico (inclusi spike).
Se iteri rapidamente su idee prodotto, un ambiente di prototipazione come Koder.ai può aiutarti a far partire un'app funzionante e validare i pattern di accesso presto: genera un frontend React con backend Go + PostgreSQL, modella alcuni endpoint reali e misura come si comportano le tue “top 5 query” prima di impegnarti in un'architettura a lungo termine. La possibilità di esportare il codice sorgente e mantenere il controllo su schema e migrazioni aiuta anche a evitare di bloccarsi.
Scrivi in anticipo cosa significa “passare”: obiettivi di latenza, tassi di errore accettabili, passi operativi richiesti (backup, cambio schema) e un costo mensile stimato all'uso previsto. Se un candidato non riesce a soddisfare un must-have nella PoC, escludilo subito.
Future-proofing non significa scegliere il database “più scalabile” al giorno 1. Significa fare scelte deliberate che ti mantengano agile quando i pattern di accesso cambiano.
Se il tuo workload è per lo più transazionale con query semplici, un database relazionale è spesso il percorso più rapido verso un prodotto affidabile. L'obiettivo è shippar con fiducia: performance prevedibile, garanzie di correttezza e tooling che il tuo team già conosce.
“Future-proof” qui significa evitare impegni irreversibili all'inizio—come adottare uno store specializzato prima di aver provato i suoi trade-off.
Costruisci un livello di accesso ai dati esplicito (o boundary di servizio) così il resto dell'app non dipende da quirks specifici del database. Centralizza la logica di query, definisci contratti (input/output) e tratta i cambi di schema come parte normale dello sviluppo.
Abitudini pratiche che aiutano nelle migrazioni:
Molti prodotti finiscono per necessitare due percorsi: OLTP per le transazioni quotidiane e analytics per reporting, esperimenti o aggregazioni pesanti. Separa quando le query analitiche iniziano a danneggiare la latenza di produzione, o quando servono retention/partizionamenti diversi.
Per tenerli allineati, standardizza le definizioni di evento/dato, automatizza le pipeline e riconcilia i totali (es.: vendite giornaliere) tra i sistemi così la “verità” non si frammenta.
Se vuoi un passo concreto, crea un modello leggero di piano di migrazione riutilizzabile dal team.
Un pattern di accesso è il modo ripetibile in cui la tua app tocca i dati in produzione: cosa legge/scrive, con quale frequenza, quanto velocemente e con quali forme di query (point lookup, scans per intervallo, join, aggregazioni, finestre temporali, ecc.). È più utile di un vago “abbiamo utenti e ordini” perché si mappa direttamente su indici, scelte di schema e idoneità del database.
Perché “popolare” riflette i vincoli di altri team, non i tuoi. Lo stesso database può essere perfetto per un carico (es. OLTP) e dannoso per un altro (es. scans analitici pesanti). Parti elencando le tue prime 5–10 letture e scritture, poi valuta i database rispetto a quei comportamenti invece che alla popolarità del brand.
Annota:
Questo diventa il tuo documento di requisiti per confrontare le opzioni.
OLTP sono molte operazioni piccole, concorrenti e sensibili alla correttezza (checkout, aggiornamenti inventario, modifiche di account) dove transazioni e vincoli contano.
OLAP/analytics sono query meno frequenti che toccano grandi volumi di dati (scans, group-by, dashboard) dove latenza di qualche secondo può andare bene, ma le scansioni pesanti sono costose.
Mescolarli nello stesso sistema spesso porta le scansioni analitiche a danneggiare la latenza lato utente.
Guarda p95/p99, non le medie. Se l'1% delle richieste più lente impiega secondi, gli utenti percepiranno l'app come inaffidabile anche se la media è buona.
Suggerimento pratico: traccia p95/p99 separatamente per endpoint critici (login, checkout, search) e correlali con metriche del database (lock, lag di replica, saturazione I/O).
Spesso hanno esigenze in competizione:
Usare store specializzati può risultare più semplice nel complesso rispetto a forzare un unico database a fare tutto con workaround.
La cache può far sembrare il carico di lettura più leggero—fino a un miss o a una purge della cache. Questo cambia le priorità:
Una cache può nascondere problemi temporaneamente, ma anche creare fallimenti a effetto valanga se i miss sovraccaricano il database.
Correttezza forte significa garanzie su transazioni e visibilità degli aggiornamenti (no stati “mezza scritti”). È cruciale per pagamenti, saldi, inventario e prenotazioni.
I compromessi possono includere:
Definisci cosa è “non può sbagliare mai” e cosa può tollerare un po’ di stalezza.
L'indicizzazione è il contratto di performance tra workload e database. Pianifica indici per:
Ma gli indici aumentano lo storage e rallentano le scritture (amplificazione di scrittura). Indicizza ciò che fai davvero spesso, non tutto.
Tratta una PoC come una mini-replica di produzione:
Se non supera un requisito indispensabile nella PoC, scartalo subito.