多租户 SaaS 数据库架构:共享库、独立库与数据隔离

多租户数据库设计的核心是在成本、隔离性、可运维性和大租户扩展之间做权衡。

多租户 SaaS 数据库架构:共享库、独立库与数据隔离

SaaS 系统通常服务多个客户。每个客户都是一个租户,租户之间要共享同一套产品能力,但数据必须隔离。

多租户数据库设计的难点在于:小租户很多时要控制成本,大租户变大后要能迁移,安全合规场景又要求更强隔离。

Rendering diagram...

方案一:共享库共享表

所有租户的数据放在同一套表里,通过 tenant_id 区分。

CREATE TABLE orders (
  id BIGINT PRIMARY KEY,
  tenant_id BIGINT NOT NULL,
  order_no VARCHAR(64) NOT NULL,
  user_id BIGINT NOT NULL,
  amount DECIMAL(12, 2) NOT NULL,
  created_at TIMESTAMP NOT NULL,
  UNIQUE (tenant_id, order_no)
);

CREATE INDEX idx_orders_tenant_created
  ON orders (tenant_id, created_at);

这种方式成本最低,开发和运维最简单。缺点是隔离性弱,所有查询都必须带 tenant_id,否则可能出现越权访问。

共享表适合早期 SaaS、小租户较多、数据规模可控的场景。

方案二:共享库独立 Schema

每个租户一个 schema,比如 tenant_1001.orderstenant_1002.orders

这种方式比共享表隔离性更好,表结构也可以在一定程度上独立演进。但 schema 数量太多后,迁移、DDL、备份和监控会变复杂。

它适合租户数量中等、对隔离性有要求但又不想每个租户独立部署数据库的场景。

方案三:每租户独立库

每个租户独立数据库,甚至独立实例。

这种方式隔离性最强,方便单租户备份、恢复、迁移和定制化,也更容易满足部分合规要求。缺点是成本高,运维复杂,租户数量多时管理压力很大。

它适合大客户、私有化部署、强合规、数据量差异很大的 SaaS。

tenant_id 是第一等字段

在共享表模式下,tenant_id 不是普通字段,而是所有数据访问的安全边界。

所有唯一键、索引、查询条件、缓存 Key、消息事件、对象存储路径,都应该包含租户维度。

cache key: tenant:{tenant_id}:user:{user_id}
kafka key: tenant_id + business_id
object path: /tenant/{tenant_id}/exports/2026-05/report.csv

如果只在 API 层过滤租户,而底层查询没有强约束,迟早会出现越权风险。

大租户迁移

共享表早期很舒服,但大租户会带来热点和容量问题。一个租户的数据量可能超过其他所有租户之和。

因此系统要预留大租户迁移能力:

  • 先识别大租户。
  • 对大租户做数据全量迁移。
  • 用 CDC 追增量。
  • 双写或双读校验。
  • 切换路由。
  • 保留回滚窗口。

租户路由表非常关键:

CREATE TABLE tenant_routes (
  tenant_id BIGINT PRIMARY KEY,
  database_type VARCHAR(32) NOT NULL,
  database_key VARCHAR(128) NOT NULL,
  status VARCHAR(32) NOT NULL
);

业务服务通过路由表决定某个租户应该访问共享库还是独立库。

权限与审计

多租户系统必须有审计日志。谁访问了哪个租户的数据,导出了哪些字段,什么时候操作,都应该能追踪。

后台管理系统尤其要谨慎。运营或客服人员跨租户查询时,必须有明确授权和审计。

小结

多租户数据库没有唯一答案:

  • 共享表成本低,适合早期和小租户。
  • 独立 Schema 隔离更强,但运维更复杂。
  • 独立库适合大客户和强合规。

成熟 SaaS 往往是混合架构:大多数租户在共享库里,大租户迁到独立库,私有化客户独立部署。

参考链接