多租户 SaaS 架构:三种模式及如何选择

多租户 SaaS 架构:三种模式及如何选择

行级租户、按租户独立 Schema,还是按租户独立数据库?你早期做出的选择决定了后续扩展的顺畅程度。

多租户架构是那种在 SaaS 开始增长前感觉很抽象的架构决策之一。随后它会影响到方方面面——你如何查询数据、扩展数据库、运行数据迁移,甚至你如何看待客户隔离。早期选对模式,你就能顺畅地扩展到数千个账户;如果选错,你就会在业务增长中期当着客户的面重写数据层。

在今天(2026年7月9日),多租户 SaaS 已经成为了常态而非特例。无论你是在构建团队工具、面向企业销售,还是提供按用量计费的服务,你都需要做出这一决策。好消息是:对于大多数产品而言,正确答案比网上说的要简单。

三种经典模式

在 SaaS 后端中隔离租户有三种成熟的方法,它们在隔离级别与运维成本之间做出了权衡。

行级租户(共享 Schema)

每个表都包含一个 tenant_id 列。每次查询都会对其进行过滤。一个数据库,一个 Schema,所有租户共享。这是最容易理解且运维成本最低的模式。

假设你正在构建一个项目管理工具。你的 tasks 表并不会切分成独立的存储空间——相反,每一行都带有属于该租户的 ID。当用户查询其任务时,应用程序会添加一个 WHERE 子句: WHERE tenant_id = current_user.tenant_id.

按租户独立 Schema

每个租户在共享数据库中拥有自己的 PostgreSQL schema。隔离性更强,因为每个 schema 都是独立的命名空间,但需要管理的对象也更多。数据库迁移变得更加复杂——你需要跨多个 schema 运行迁移。这种模式介于行级隔离和完全隔离之间。

按租户独立数据库

每个租户拥有专属的数据库或实例。隔离性最高——一个租户的数据保存在完全独立的存储中。但运维负担也是最大的——你需要为每个客户管理独立的数据库实例、备份和升级。

为什么行级租户最适合大多数 SaaS

对于绝大多数 B2B SaaS 产品来说,行级多租户是正确的默认选择。它的运维成本最低,数据库迁移最容易运行,而且其扩展能力超乎创始人的预料。

人们总是会质疑:“但是隔离性怎么办?”而这正是 Postgres 给出强有力解答的地方。

行级安全性 (RLS) 与 Postgres

Postgres 提供了一个称为行级安全性(Row-Level Security,简称 RLS)的功能。RLS 允许数据库本身强制执行规定:查询只能看到属于当前租户的行。你只需设置一次策略(直接在数据库中),即使存在漏洞的查询也不会跨租户泄露数据。

Supabase 作为一个托管式 Postgres 平台,将 RLS 作为原生模型。你只需定义策略,数据库就成为了安全边界,而不仅仅依赖应用程序层。

结合在每个表上的 tenant_id 以及以其开头的索引,这种模式可以轻松服务于庞大的客户群。数据库承担了强制约束,应用程序无需时刻记住添加过滤条件。

关于 RLS 的真实警示

来自实际经验的一个重要细节:编写 RLS 策略时,务必让辅助函数在每次查询时仅运行一次,而不是在每一行都运行一次。如果策略在每一行都重新评估查询,随着表体积的增大,原本快速的 API 端点会在不知不觉中变慢。解决办法是包裹检查逻辑,使查询规划器将其作为 init-plan 执行——在开始时进行一次性检查,而不是按行检查。

何时升级到更强的隔离级别

行级隔离适用于大多数场景。但有些客户需要更高的隔离级别。

请经过深思熟虑后再进行升级,而不是盲目反射性升级:

  • 法规或合同要求的隔离 ——客户要求将其数据保存在物理上独立的数据库中。这可能是因为他们处于受监管的行业,或者合同条款中有此要求。
  • “嘈杂邻居”(Noisy-neighbor)风险 ——单个巨头客户的工作负载降低了其他所有人的性能。独立的基建可以解决这一问题。
  • 按租户定制 ——Schema 层面发生了真正的分化,而不仅仅是数据不同。你在为不同的客户存储本质上完全不同的数据结构。

即便如此,混合模式效果也很好:将大多数租户保持在行级隔离,仅将最大或最敏感的账户升级到独立数据库。

至关重要的设计原则

无论你做出何种选择,都要尽早将多租户架构融入系统设计中。不要等到以后再硬塞进去。

在所有重要位置添加 tenant_id

添加 tenant_id 到每个领域表中,并将其放在复合索引的首位。这能提高查询速度,并自然地按租户组织你的数据。

绝不要信任客户端提供的租户身份

务必从已验证的会话(session)中提取租户身份,而不是从请求参数或 cookie 中提取。如果你询问客户端“你是哪个租户?”,恶意客户端或存在 bug 的客户端可能会作假。

在数据库层强制执行隔离

不要盲目信任应用程序能时刻记住包含 WHERE 子句。利用数据库约束和 RLS 来彻底消除数据泄露的可能。如果开发人员在某处漏掉了过滤条件,数据库本身也能防止错误发生。

让租户预配(Provisioning)成为单一且经过测试的代码路径

当新增租户时,请通过单一、清晰且经过测试的流程来执行。不要让代码库的不同部分以不同的方式创建租户。一致性可以预防 bug。

代价最为惨重的错误

错误不在于选错了“模型”,而在于将租户机制保持隐式,并将隔离逻辑散落于代码库各处。最终你会发现在某些 API 端点里用 WHERE 子句,在另一些地方用 SQL join,却没有统一明确的标准。

集中管理多租户机制。在数据库层面强制执行。一次性设置好策略,并在此基础上构建系统。这样你就能保留后续演进的自由度。

结语

多租户架构是一项极其基础的架构选择。基于 Postgres 行级安全性(RLS)的行级租户模式是大多数 SaaS 的正确默认选择——它成本低廉、易于扩展,且由数据库强制执行隔离。只有在有明确理由(如法规监管、性能隔离或真正的 Schema 分化)时,才升级到更强的隔离模式。从第一天起就将其融入设计并记录你的选择,你就能顺畅无阻地完成扩展。

优点

  • 对大多数产品而言,行级租户模式是运维成本最低且最简单的选择。
  • Postgres 行级安全性将隔离逻辑下沉至数据库层,由数据库透明地强制执行。
  • 单一数据库、单一 Schema 使得迁移和备份操作简单直接。
  • 你可以在日后将特定租户升级到更强的隔离模式,而无需重构整体架构。
  • 这种 tenant_id + 索引首列的模式能够轻松扩展以支撑庞大的客户群。

缺点

  • 对于要求物理数据隔离的法规或合同要求而言,行级隔离是不够的。
  • 单个“嘈杂”租户的高负载查询可能会影响同一数据库上的其他租户。
  • RLS 策略上的错误(例如逐行重新评估逻辑)可能会在不知不觉中严重拖垮性能。
  • 日后从行级隔离迁移到按租户独立 Schema 或按租户独立数据库较为复杂且存在风险。
  • 开发人员必须严格自律地始终包含租户过滤条件——虽然数据库能有所帮助,但应用程序级别的 bug 仍有可能发生。

注意事项

本文仅供教育参考,基于通用最佳实践编写。素材来源于博客文章;在做出架构决策之前,应结合原出版物和您自身的具体需求核实相关说法。法规与合规要求因行业和司法管辖区而异——请针对您的具体使用场景咨询法律与安全专家。请在您自己的环境中彻底测试 RLS 策略,尤其是大规模下的性能表现。对于关键业务系统,本文不能替代专业的架构审查。

常见问题

  • 什么是 SaaS 中的多租户架构?为什么它很重要?
  • Postgres 中的行级安全性如何防止租户之间的数据泄露?
  • 我应该在什么时候使用按租户独立 Schema,而不是行级租户?
  • 什么是“嘈杂邻居”(Noisy-neighbor)问题?它如何影响 SaaS 架构?
  • 如何向现有单租户数据库中添加 tenant_id?
  • 我可以先从行级隔离开始,日后再升级到按租户独立数据库吗?
  • 规模化使用时,RLS 策略对性能有何影响?
  • 如何在应用程序中测试多租户隔离?

标签

#saas #architecture #postgres #scaling #multitenant #database #security #rls

Free field guide

API Security Testing Checklist

A practical workflow for testing authentication, authorization, input handling, business logic, and evidence without losing track of scope.