skip to content

Compare the three common ways to lay out data for many customer organizations in a relational system: all tenants sharing tables with a tenant_id column, one schema per tenant inside a shared database, and one database per tenant. How do they differ in isolation, cost per tenant, and operational effort?

level: middleimportance: must knowfreq 62%

answer

  1. pooled -> siloed: density vs isolation
  2. tenant_id = cheapest, leak risk, hard per-tenant restore
  3. schema-per-tenant = catalog bloat + migration fan-out
  4. db-per-tenant = easy restore/residency, high fixed cost
  5. hybrid: pool the tail, silo the whales

basics

~20 s

Shared tables are cheapest and densest but rely on code for isolation and make per-tenant restore hard. Schema-per-tenant separates data structurally but fans out migrations and bloats the catalog. Database-per-tenant gives the strongest isolation and easy per-tenant backup, at the highest fixed cost per tenant.

solid answer

~60 s

It is one axis - pooled to siloed - trading density for isolation. **Shared tables + tenant_id:** highest density, one migration, one connection pool; idle tenants cost almost nothing. Isolation depends entirely on every query carrying the tenant predicate, so a single bug is a cross-tenant leak. Per-tenant restore means extracting rows from a full restore, noisy neighbors share the buffer pool, and optimizer statistics describe an average tenant. **Schema per tenant:** same instance, separate namespaces. Isolation is structural, per-tenant export is a schema dump, and per-tenant customization is possible. Cost: migrations fan out over N schemas, the catalog holds N x tables (planning, maintenance and dump cost grow), and resource contention is unchanged. **Database per tenant:** strongest blast-radius isolation, independent backups and point-in-time restore, per-tenant residency and resource limits, trivial hard delete. But each database carries fixed memory, connection and backup overhead, so thousands of small tenants are uneconomic, and cross-tenant reporting needs a separate pipeline. Most SaaS pools the long tail and silos large or regulated tenants.

go deeper

for a junior

Name the three layouts and give one honest advantage and disadvantage of each; density versus isolation is the core idea.

for a middle

Compare on concrete axes - isolation, cost per tenant, migrations, backup and restore, noisy neighbors - and pick one for a stated tenant profile.

for a senior

Bring in operational reality: migration fan-out, catalog bloat, connection pooling, statistics skew, and the tooling needed for per-tenant restore and deletion.

for a principal

Argue the hybrid: pool the long tail, silo regulated or whale tenants, keep a tenant-location directory and export/import tooling so the choice stays reversible, and tie the tiers to pricing.

## The single axis The three layouts are points on one axis from **pooled** (everything shared) to **siloed** (everything separate). Moving toward siloed buys isolation and per-tenant operability; moving toward pooled buys density and operational simplicity at scale. Nothing else about the schema needs to change: in all three, the application logically works on "this tenant's data". ## Shared tables with a tenant_id column (pooled) All tenants' rows live in the same tables, distinguished by a discriminator column that must lead keys and indexes and appear in every predicate. *Isolation:* logical only. Correctness rests on the application, and optionally on row-level security policies. A forgotten predicate leaks or corrupts other customers' data. *Cost:* the best. One set of tables, one connection pool, one buffer cache. Ten thousand dormant tenants cost close to nothing, which is what makes self-serve and free tiers viable. *Operations:* one migration for everyone - simple, but with a blast radius of *all* tenants and no way to canary a schema change on one customer. Per-tenant point-in-time restore is surgery: restore the whole database to a side copy, extract that tenant's rows, and merge them back respecting foreign-key order. Hard-deleting a departing tenant is a fan-out of DELETEs that leaves bloat. Noisy neighbors are real: one tenant's report evicts everyone's cached pages and consumes shared CPU and I/O. Table-level statistics reflect the average tenant, so plans can be poor for outliers. ## Schema per tenant Each tenant gets its own namespace of identically named tables inside one database instance. *Isolation:* structural for data - a query in one schema cannot accidentally see another - and grantable per schema. Instance resources (CPU, memory, I/O, connections) are still shared, so it does nothing about noisy neighbors. *Cost:* still a single instance, so reasonably dense, but the **catalog** now holds N x (tables + indexes + constraints). At a few thousand tenants this is felt: slower catalog lookups and plan caching, far more objects for vacuum and statistics maintenance, and dumps that enumerate everything. *Operations:* per-tenant export and restore is a schema dump and load - much easier than the pooled case. Migrations become a fan-out job over N schemas that must be resumable, idempotent, and tolerant of tenants sitting at mixed versions mid-rollout, which forces backward-compatible (expand/contract) changes. You can canary a change on a few tenants. Schema drift is the chronic disease: a hotfix applied to one tenant and forgotten becomes an outage months later. Connection pooling is trickier because pools are typically per database, with the schema selected per session - which, over a shared pool, must be reset per transaction. ## Database per tenant Each tenant gets its own database, possibly its own instance or its own region. *Isolation:* the strongest short of separate hardware - separate credentials, separate storage, often separate resource limits. It answers enterprise security questionnaires and data-residency requirements directly, and it caps blast radius: corruption or a runaway query hurts one customer. *Cost:* the worst per tenant. Each database, and especially each instance, carries fixed memory, background processes, connection slots, monitoring and backup jobs. Thousands of tiny tenants become both expensive and operationally unmanageable, and "one instance per tenant" runs into cloud quota limits. *Operations:* per-tenant backup, point-in-time restore, clone-for-support and hard delete (drop the database) are trivial - the biggest practical win. Migrations fan out with the same problems as schema-per-tenant, plus deployment plumbing across many endpoints and credentials. Cross-tenant questions ("total usage this month") require an aggregation pipeline into a warehouse, because there is no single place to query. ## Choosing, and the hybrid Decide with: contractual and regulatory isolation demands, tenant count and size distribution, how much per-tenant restore and deletion matter, and the team's automation maturity. Long-tail SaaS with 50,000 small tenants: pool. A dozen banks: silo. The common production answer is **hybrid** - pooled tables by default, promoting whales and regulated customers into their own database, with a tenant-to-location directory the application consults so moving a tenant is a data operation rather than a code change. Whatever you pick, keep one identical schema everywhere and build tenant export/import tooling early: it is the same tool used for restore, offboarding, and migration between models.

  • A SaaS has 40,000 tenants of which 39,000 are tiny. Which layout would you start with, and why?
    Pooled shared tables with tenant_id, because per-tenant fixed cost dominates at that tenant count and idle tenants must be nearly free. Invest the savings in a strict tenant-context choke point plus row-level security, cross-tenant isolation tests, and per-tenant export and restore tooling. Keep a tenant-to-location directory so the handful of tenants that later demand isolation can be moved into their own database without application changes.
  • What breaks first when schema-per-tenant grows to tens of thousands of schemas?
    Catalog and maintenance overhead: the instance now tracks hundreds of thousands of tables and indexes, which slows planning and catalog lookups, multiplies vacuum and statistics work, and makes dumps and schema-diff tooling crawl. Operationally, migration fan-out time grows linearly and schema drift between tenants becomes chronic. That is usually the point where teams move the largest tenants to their own databases and pool the rest.

Shared tables are bunk beds in one dorm room: cheapest per person, but everyone hears everyone. Schema-per-tenant is separate apartments in one building: your own front door, still one boiler and one power feed. Database-per-tenant is a detached house: nobody bothers you, and you pay for a whole house whether or not you are home.

saying these in an interview costs you the question

  • Claiming database-per-tenant 'scales better' without accounting for fixed per-database overhead
  • Assuming schema-per-tenant solves noisy neighbors - the same instance resources are shared
  • Believing pooled tables make per-tenant restore impossible rather than expensive
  • Allowing per-tenant schema customization and then being surprised by drift
  • Treating the choice as permanent instead of building tenant move and export tooling

context