Scopri come i database multi-tenant influenzano sicurezza e prestazioni, i rischi principali (isolamento, noisy neighbors) e i controlli pratici per mantenere i tenant sicuri e reattivi.

Un database multi-tenant è una configurazione in cui molti clienti (tenant) condividono lo stesso sistema di database—lo stesso server di database, lo stesso storage sottostante e spesso lo stesso schema—mentre l'applicazione garantisce che ogni tenant possa accedere solo ai propri dati.
Pensalo come un condominio: tutti condividono la struttura e le utenze dell'edificio, ma ogni tenant ha la propria unità chiusa a chiave.
In un approccio single-tenant, ogni cliente riceve risorse di database dedicate—per esempio, la propria istanza di database o il proprio server. L'isolamento è più semplice da ragionare, ma di solito è più costoso e operativo mano a mano che il numero dei clienti cresce.
Con la multi-tenancy, i tenant condividono l'infrastruttura, il che può essere efficiente—ma significa anche che il tuo design deve imporre confini in modo intenzionale.
Le aziende SaaS spesso scelgono la multi-tenancy per motivi pratici:
La multi-tenancy di per sé non è automaticamente "sicura" o "veloce." I risultati dipendono da scelte come come i tenant sono separati (schema, righe o database), come viene applicato il controllo accessi, come vengono gestite le chiavi di cifratura e come il sistema impedisce che il carico di un tenant rallenti gli altri.
Il resto di questa guida si concentra su quelle scelte di design—perché nei sistemi multi-tenant, sicurezza e prestazioni sono funzionalità che costruisci, non assunzioni preesistenti.
La multi-tenancy non è una singola scelta di design—è uno spettro di quanto strettamente condividi l'infrastruttura. Il modello che scegli definisce il tuo confine di isolamento (cosa non deve mai essere condiviso) e questo influenza direttamente sicurezza, isolamento delle prestazioni e operazioni quotidiane.
Ogni tenant ottiene il proprio database (spesso sullo stesso server o cluster).
Confine di isolamento: il database stesso. È solitamente la storia di isolamento più pulita perché l'accesso cross-tenant richiede tipicamente il superamento del confine del database.
Compromessi operativi: più pesante da gestire a scala. Aggiornamenti e migrazioni di schema possono dover essere eseguiti migliaia di volte, e il connection pooling può complicarsi. Backup/restore sono semplici a livello tenant, ma l'overhead di storage e gestione può crescere rapidamente.
Sicurezza e ottimizzazione: generalmente più semplice da mettere in sicurezza e ottimizzare per cliente, e adatto quando i tenant hanno requisiti di compliance differenti.
I tenant condividono un database, ma ciascuno ha il proprio schema.
Confine di isolamento: lo schema. È una separazione significativa, ma si basa su permessi e tooling corretti.
Compromessi operativi: aggiornamenti e migrazioni sono ancora ripetitive, ma meno pesanti rispetto al database-per-tenant. I backup sono più complicati: molti strumenti trattano il database come unità di backup, quindi operazioni a livello tenant possono richiedere esportazioni dello schema.
Sicurezza e ottimizzazione: più facile far rispettare l'isolamento rispetto alle tabelle condivise, ma serve disciplina sui privilegi e verifica che le query non facciano riferimento allo schema sbagliato.
Tutti i tenant condividono database e schema, ma ogni tenant ha tabelle separate (es. orders_tenant123).
Confine di isolamento: l'insieme di tabelle. Può funzionare per pochi tenant, ma scala male: metadata ingombranti, script di migrazione scomodi e degradazione del piano di query.
Sicurezza e ottimizzazione: i permessi possono essere precisi, tuttavia la complessità operativa è alta e aggiungere nuove tabelle o funzionalità può facilmente introdurre errori.
Tutti i tenant condividono le stesse tabelle, distinti tramite una colonna tenant_id.
Confine di isolamento: il livello di query e il layer di controllo accessi (comunemente row-level security). Questo modello è efficiente operativamente—uno schema da migrare, una strategia di indici da gestire—ma è il più esigente per sicurezza e isolamento delle prestazioni.
Sicurezza e ottimizzazione: è il più difficile da fare bene perché ogni query deve essere tenant-aware, e il problema del noisy neighbor è più probabile a meno di throttling delle risorse e indici curati.
Una regola utile: più condividi, più le upgrade diventano semplici—ma più rigore serve nei controlli di isolamento e nelle misure di performance.
La multi-tenancy non significa solo “più clienti in un database”. Cambia il tuo modello di minacce: il rischio maggiore si sposta dagli esterni che violano il sistema a utenti autorizzati che, per errore o intenzionalmente, vedono dati appartenenti a un altro tenant.
L'autenticazione risponde a “chi sei?”, l'autorizzazione a “cosa puoi fare?”. In un database multi-tenant, il contesto tenant (tenant_id, account_id, org_id) deve essere applicato durante l'autorizzazione—non trattato come un filtro opzionale.
Un errore comune è presumere che una volta autenticato l'utente e noto il suo tenant, l'app terrà automaticamente le query separate. In pratica, la separazione deve essere esplicita e applicata in un punto di controllo coerente (es. policy DB o un layer di query obbligatorio).
La regola più semplice e importante: ogni SELECT e ogni UPDATE/DELETE deve essere limitato esattamente a un tenant.
Questo vale per:
Se lo scoping del tenant è opzionale, prima o poi verrà omesso.
I leak cross-tenant spesso derivano da piccoli errori di routine:
tenant_id sbagliatoI test solitamente girano con dataset piccoli e ipotesi pulite. La produzione aggiunge concorrenza, retry, cache, dati misti e edge case reali.
Una feature può passare i test perché nel DB di test c'è un solo tenant o i fixture non includono ID sovrapposti tra tenant. I design più sicuri rendono difficile scrivere una query non scoperte, invece di affidarsi ai revisori per individuarla ogni volta.
Il rischio centrale in un database multi-tenant è semplice: una query che dimentica di filtrare per tenant può esporre i dati di un altro. Forti controlli di isolamento presumono che gli errori capitino e rendono quegli errori innocui.
Ogni record appartenente a un tenant dovrebbe avere un identificatore tenant (per esempio tenant_id) e il tuo layer di accesso ai dati dovrebbe sempre scoprire letture e scritture tramite esso.
Un pattern pratico è “tenant context first”: l'app risolve il tenant (da sottodominio, org ID o claim nel token), lo memorizza nel contesto della richiesta e il codice d'accesso ai dati rifiuta di eseguire senza quel contesto.
Guardrail utili:
tenant_id nelle chiavi primarie/uniche dove appropriato (per evitare collisioni)tenant_id così da non creare relazioni cross-tenant accidentalmenteDove supportato (notably PostgreSQL), la row-level security può spostare i controlli tenant nel database. Le policy possono limitare SELECT/UPDATE/DELETE in modo che siano visibili solo le righe corrispondenti al tenant corrente.
Questo riduce la dipendenza dal fatto che “ogni sviluppatore ricordasse la WHERE”, e può proteggere contro alcuni scenari di injection o uso improprio degli ORM. Considera RLS come un secondo lucchetto, non l'unico.
Se i tenant hanno dati più sensibili o requisiti di compliance più severi, separare per schema (o per database) può ridurre il raggio d'impatto. Il contro è un aumento dell'overhead operativo.
Progetta permessi in modo che il default sia “nessun accesso”:
Questi controlli funzionano meglio insieme: forte scoping tenant, policy imposte dal DB quando possibile e privilegi conservativi che limitano i danni quando qualcosa scivola.
La cifratura è uno dei pochi controlli che aiuta anche quando altri livelli di isolamento falliscono. In un datastore condiviso, l'obiettivo è proteggere i dati mentre si muovono, mentre sono a riposo e mentre l'app dimostra per quale tenant sta agendo.
Per i dati in transito, richiedi TLS per ogni hop: client → API, API → database e per qualsiasi chiamata interna tra servizi. Impònga TLS a livello di database dove possibile (per esempio rifiutando connessioni non TLS) così che "eccezioni temporanee" non diventino permanenti.
Per i dati a riposo, usa cifratura a livello di database o storage (managed disk encryption, TDE, backup cifrati). Questo protegge contro media persi, esposizione di snapshot e alcune classi di compromesso infrastrutturale—ma non impedirà a una query difettosa di restituire righe di un altro tenant.
Una singola chiave condivisa è più semplice da operare (meno chiavi da ruotare, meno punti di errore). Lo svantaggio è il raggio d'impatto: se quella chiave è esposta, tutti i tenant sono esposti.
Le chiavi per-tenant riducono il blast radius e possono soddisfare richieste enterprise (alcuni clienti vogliono controllo chiave separato). Il compromesso è complessità: ciclo di vita delle chiavi, rotazioni e workflow di supporto (es. cosa succede se un tenant disabilita la propria chiave).
Un compromesso pratico è l'envelope encryption: una chiave master cifra le chiavi dati per tenant, mantenendo gestibile la rotazione.
Conserva le credenziali del database in un secrets manager, non in variabili d'ambiente in configurazioni a lunga vita. Preferisci credenziali a vita breve o rotazione automatica e assegna accesso per ruolo così che una compromissione in un componente non raggiunga automaticamente ogni database.
Tratta l'identità del tenant come critica per la sicurezza. Non accettare mai un tenant ID grezzo dal client come verità. Associa il contesto tenant a token firmati e controlli server-side e convalida tutto ad ogni richiesta prima di qualsiasi chiamata al database.
La multi-tenancy cambia cosa significa “normale”. Non stai solo osservando un database—stai osservando molti tenant che condividono lo stesso sistema, dove un errore può trasformarsi in una esposizione cross-tenant. Una buona auditabilità e monitoraggio riducono sia la probabilità sia il raggio d'impatto degli incidenti.
Al minimo, registra ogni azione che può leggere, modificare o concedere accesso ai dati dei tenant. Gli eventi di audit più utili rispondono a:
Registra anche azioni amministrative: creazione tenant, modifica di policy di isolamento, cambio di RLS, rotazione chiavi e modifica delle stringhe di connessione.
Il monitoraggio dovrebbe rilevare pattern insoliti per un uso SaaS sano:
Collega gli alert a runbook azionabili: cosa controllare, come contenere e chi avvisare.
Tratta l'accesso privilegiato come una modifica in produzione. Usa ruoli a privilegi minimi, credenziali a vita breve e approvazioni per operazioni sensibili (cambi di schema, esportazioni dati, modifiche policy). Per emergenze, mantieni un account break-glass strettamente controllato: credenziali separate, ticket/approvazione obbligatori, accesso limitato nel tempo e logging extra.
Imposta la retention basata sui requisiti di compliance e investigazione, ma limita l'accesso ai log così che lo staff di supporto veda solo i log del proprio tenant. Quando i clienti richiedono esportazioni di audit, fornisci report filtrati per tenant invece di log grezzi condivisi.
La multi-tenancy migliora l'efficienza permettendo a molti clienti di condividere la stessa infrastruttura database. Il compromesso è che le prestazioni diventano un'esperienza condivisa: quello che fa un tenant può influenzare gli altri, anche se i loro dati sono isolati.
Un "noisy neighbor" è un tenant la cui attività è così intensa (o così a picchi) da consumare più risorse condivise del dovuto. Il database non è "rotto"—è semplicemente occupato a gestire quel carico, quindi gli altri tenant aspettano di più.
Pensalo come un edificio dove un'unità apre contemporaneamente più docce e la lavatrice; gli altri avvertono una pressione dell'acqua minore.
Anche quando ogni tenant ha righe o schemi separati, molti componenti critici per le prestazioni sono condivisi:
Quando queste risorse si saturano, la latenza cresce per tutti.
Molti carichi SaaS arrivano a scatti: import massivi, report di fine mese, campagne marketing o cron job all'inizio dell'ora.
I picchi possono creare "ingorghi" nel database:
Anche se il picco dura pochi minuti, può causare ritardi a catena mentre le code si svuotano.
Dal lato cliente, i problemi da noisy neighbor sembrano casuali e ingiusti. Sintomi comuni:
Questi sintomi sono segnali che servono tecniche di isolamento delle risorse (vedi dopo), non solo "più hardware".
La multi-tenancy funziona meglio quando un cliente non può prendere in prestito più della sua quota di capacità database. L'isolamento delle risorse è l'insieme di guardrail che impedisce a un tenant pesante di rallentare tutti gli altri.
Un errore comune è avere connessioni senza limiti: un picco di traffico di un tenant apre centinaia di sessioni e prosciuga il database.
Imposta limiti fissi in due punti:
Anche se il DB non può imporre direttamente "connessioni per tenant", puoi approssimarlo instradando ciascun tenant attraverso un pool dedicato o una partizione del pool.
Il rate limiting è questione di equità nel tempo. Applicalo vicino al bordo (API gateway/app) e, dove supportato, dentro il database (resource groups/workload management).
Esempi:
Proteggi il database da query "runaway":
Questi controlli dovrebbero degradare in modo elegante: restituire un errore chiaro e suggerire retry/backoff.
Sposta il traffico di sola lettura lontano dal primario:
L'obiettivo non è solo velocità—è ridurre lock e contesa CPU affinché i tenant rumorosi abbiano meno modi di impattare gli altri.
I problemi di prestazioni multi-tenant spesso sembrano “il database è lento”, ma la causa reale è di solito il modello dati: come i dati dei tenant sono indicizzati, filtrati, e disposti fisicamente. Un buon modello rende le query tenant-scoped naturalmente veloci; un modello scarso forza il DB a lavorare troppo.
La maggior parte delle query SaaS dovrebbe includere un identificatore tenant. Modellalo esplicitamente (per esempio tenant_id) e crea indici che lo inizino. Nella pratica, un indice composito come (tenant_id, created_at) o (tenant_id, status) è molto più utile dell'indicizzare solo created_at o status isolati.
Vale anche per l'unicità: se le email sono uniche solo per tenant, applicala con (tenant_id, email) piuttosto che un vincolo globale su email.
Un pattern comune di query lente è una scansione cross-tenant accidentale: una query che dimentica il filtro tenant e tocca una grande porzione della tabella.
Rendi la via sicura quella semplice:
Il partizionamento può ridurre la quantità di dati che ogni query deve considerare. Partiziona per tenant quando i tenant sono grandi e squilibrati. Partiziona per tempo quando l'accesso è principalmente recente (eventi, log, fatture), spesso con tenant_id come colonna d'indice principale dentro ogni partizione.
Considera lo sharding quando un singolo database non tiene il passo con il throughput di picco, o quando il carico di un tenant minaccia gli altri.
I "hot tenant" appaiono per volumi di letture/scritture sproporzionati, contesa di lock o indici sovradimensionati.
Individua i hot tenant monitorando tempo query per tenant, righe lette e tassi di scrittura. Quando un tenant domina, isolalo: spostalo su uno shard/database separato, dividi tabelle grandi per tenant o introduce cache e rate limit dedicati così gli altri tenant mantengano performance.
La multi-tenancy raramente fallisce perché il database "non può farlo". Fallisce quando le operazioni quotidiane permettono a piccole incoerenze di trasformarsi in lacune di sicurezza o regressioni di performance. L'obiettivo è rendere la via sicura il comportamento predefinito per ogni cambiamento, job e deploy.
Scegli un identificatore tenant canonico (es. tenant_id) e usalo coerentemente su tabelle, indici, log e API. La coerenza riduce sia errori di sicurezza (query verso il tenant sbagliato) sia sorprese di performance (indici compositi mancanti).
Salvaguardie pratiche:
tenant_id in tutti i percorsi di accesso primari (query, repository, scope ORM)tenant_id per ricerche comunitenant_id, o check constraint) per intercettare scritture errate prestoI worker asincroni sono fonte comune di incidenti cross-tenant perché operano "out of band" rispetto alla richiesta che ha stabilito il contesto tenant.
Pattern operativi utili:
tenant_id esplicitamente in ogni payload di job; non fare affidamento su contesto ambientaletenant_id all'inizio/fine job e ad ogni retry così le indagini possono rapidamente delimitare l'impattoLe migrazioni di schema e dati dovrebbero essere distribuibili senza un rollout perfettamente sincronizzato.
Usa cambiamenti in rolling:
Aggiungi test negativi automatici che tentano intenzionalmente di accedere ai dati di un altro tenant (lettura e scrittura). Trattali come blocchi di rilascio.
Esempi:
tenant_id disallineato e verificare il fallimento nettoI backup sono facili da descrivere ("copia il database") e sorprendentemente difficili da eseguire in sicurezza in un database multi-tenant. Nel momento in cui molti clienti condividono tabelle, serve un piano per recuperare un tenant senza esporre o sovrascrivere altri.
Un backup completo del database resta la base per il disaster recovery, ma non basta per i casi di support day-to-day. Approcci comuni:
tenant_id) per ripristinare i dati di un singolo tenantSe fai affidamento su export logici, tratta il job di export come codice di produzione: deve imporre isolamento tenant (es. tramite RLS) invece di fidarsi di una WHERE scritta una volta e dimenticata.
Le richieste di privacy (export, delete) toccano sicurezza e prestazioni. Costruisci workflow ripetibili e auditati per:
Il rischio maggiore non è un hacker—è un operatore frettoloso. Riduci l'errore umano con guardrail:
Dopo un disaster recovery drill, non fermarti a “l'app è su”. Esegui controlli automatici che confermino l'isolamento tenant: query campione tra tenant, revisione dei log di audit e verifica che chiavi di cifratura e ruoli d'accesso siano ancora correttamente scoperte.
La multi-tenancy è spesso la scelta predefinita migliore per SaaS, ma non è una decisione permanente. Man mano che prodotto e mix di clienti evolvono, l'approccio del "datastore condiviso" può iniziare a creare rischi di business o rallentare il delivery.
Considera di passare a più isolamento quando uno o più di questi segnali si presentano costantemente:
Non devi scegliere tra "tutto condiviso" e "tutto dedicato". Approcci ibridi comuni:
Più isolamento solitamente significa spesa infrastrutturale maggiore, maggiore overhead operativo (migrazioni, monitoraggio, on-call) e più coordinamento di release (cambi schema su più ambienti). Il trade-off è garanzie di prestazioni più chiare e conversazioni di compliance più semplici.
Se stai valutando opzioni di isolamento, rivedi le guide correlate nella sezione /blog o confronta piani e opzioni di deployment nella pagina /pricing.
Se vuoi prototipare un SaaS velocemente e testare le ipotesi multi-tenant precocemente (scoping tenant, schemi compatibili con RLS, throttling e workflow operativi), una piattaforma di prototipazione come Koder.ai può aiutarti a creare un'app React + Go + PostgreSQL funzionante dalla chat, iterare in modalità planning e distribuire con snapshot e rollback—quindi esportare il codice sorgente quando sei pronto a consolidare l'architettura per la produzione.
Un database multi-tenant è un'architettura in cui più clienti condividono la stessa infrastruttura di database (e spesso lo stesso schema), mentre l'applicazione e/o il database assicurano che ogni tenant possa accedere solo ai propri dati. Il requisito fondamentale è lo scoping rigoroso del tenant su ogni lettura e scrittura.
La multi-tenancy viene spesso scelta per:
Il compromesso è che bisogna costruire intenzionalmente guardrail per l'isolamento e le prestazioni.
I modelli comuni (da maggiore isolamento a maggiore condivisione) sono:
La scelta determina il confine di isolamento e il carico operativo.
Il rischio principale si sposta verso accessi cross-tenant causati da errori di routine, non solo da attacchi esterni. Il contesto tenant (come tenant_id) deve essere trattato come un requisito di autorizzazione, non come un filtro opzionale. Serve considerare anche le condizioni reali di produzione: concorrenza, caching, retry e job in background.
Le cause più comuni includono:
tenant_id sbagliatoProgetta guardrail in modo che le query non scoperte siano difficili (o impossibili) da eseguire.
La row-level security (RLS) sposta i controlli di tenant dentro il database con policy che limitano SELECT/UPDATE/DELETE alle righe del tenant corrente. Riduce la dipendenza dal fatto che "tutti ricordino la clausola WHERE", ma dovrebbe essere affiancata da scoping a livello app, privilegi minimi e test robusti. Tratta RLS come un lucchetto aggiuntivo, non come l'unico.
Una baseline pratica include:
tenant_id canonico nelle tabelle possedute dai tenanttenant_idL'obiettivo è far sì che gli errori falliscano in modo sicuro.
La cifratura aiuta ma copre rischi diversi:
Tratta l'identità del tenant come critica: non fidarti di un tenant ID grezzo dal client; legalo a token firmati e controlli server-side.
Il problema del noisy neighbor si verifica quando un tenant consuma risorse condivise (CPU, memoria, I/O, connessioni) rallentando gli altri. Mitigazioni pratiche:
Punta all'equità, non solo alla massima potenza.
È il momento di aumentare l'isolamento quando:
Opzioni ibride comuni: spostare pochi tenant top-tier su DB/cluster separati, piani a livelli (shared vs dedicated), o isolare l'analytics/reporting in store separati.