↓ 跳过正文

SaaS Architecture Single-Tenant

单租户 vs 多租户 SaaS:什么时候用哪种

单租户(多实例)和多租户 SaaS 到底有什么区别?从隔离、成本、运维三方面对比,介绍两者之间的中间方案、什么时候用哪种、怎么在共享数据库里隔离租户,以及让“每个客户一套实例”撑得下去的工具。

单租户 vs 多租户 SaaS:什么时候用哪种

单租户 SaaS 给每个客户一整套独立的应用和数据库。多租户 SaaS 让所有客户共用一套应用和数据库,靠租户 ID 区分,而且每条查询都得检查它。单租户换来的是隔离、按客户的控制权和更省事的合规,代价是要运维一大堆副本。多租户更便宜,运维也简单得多,但把客户彼此隔开就成了代码的活儿。大多数产品从多租户起步,只给少数真正需要的客户提供独享实例。

做B2B SaaS,租户架构这个问题迟早会找上门,通常就是某个大客户问“我们的数据能不能和别人分开放”的那一天。这种讨论我经历过不止一次。答案基本就两个:给每个客户独立的实例(单租户,多实例),或者让所有人共享一个实例(多租户,单实例)。没有哪个是“正确答案”,只是坏起来的方式不一样。

基础设施设计
#

单租户,多实例
#

单租户,多实例:在这种架构下,多套基础设施实例分别服务多个租户。

这条路的吸引力就在“隔离”二字,几乎所有好处都由此而来。每个客户的数据都在自己独立的环境里,安全审查和合规谈判会轻松很多。一次糟糕的部署或一个吃资源的任务,只会影响一个客户,而不是全部。每个实例可以独立扩容、调优、定制;资源专用,性能不会因为隔壁租户在跑什么而波动。维护也能按客户逐个安排,停机影响小而可控。定价合理的话,还能让基础设施成本对上每个租户的实际用量。

代价是:所有东西你都要运维N份。资源开销不断累积,实例之间的配置慢慢漂移,让整个集群的版本和数据保持一致本身就成了一个项目。扩容、隔离、成本管理的问题不会消失,只会成倍增加。

多租户,单实例
#

多租户,单实例:一套应用程序同时服务多个租户。

它是默认选项不是没有原因的:一套部署、一个要备份的数据库、生产环境只有一个版本。资源利用率高,运维简单,所有客户同时拿到更新和安全补丁。

难点是共享的另一面。一个租户用得猛,所有人的性能都会被拖累;按客户定制的空间有限;数据隔离不再靠基础设施,而是落在你的应用代码里,一个bug就可能变成跨租户的数据泄露。扩容瓶颈一来就是所有客户一起受影响,维护也要更小心,因为再也没有“只影响这一个客户”的爆炸半径了。

延伸阅读:SaaS 方案中的多实例与多租户架构

多租户 vs 多实例
#

这个选择也常被说成“多租户 vs 多实例”,其实是同一件事从另一面看。“租户”说的是客户:一套软件有几个客户在共用。“实例”说的是部署:你跑了几套软件。单租户产品就是每个客户一套的多实例部署,多租户产品通常是大家共用的单实例。所以有人要“多实例”,意思就是要单租户。

实际上它更像一条光谱,而不是二选一,真正的决定大多落在数据库上。最共享的一端,是所有客户的数据行放在同一批表里,每行带一个 tenant_id 列,每条查询都按它过滤。运行成本最低,也最容易出错。往前一步,是在共享数据库里给每个客户单独一个 schema:隔离更干净,也能按客户备份,代价是每次迁移都要按 schema 的数量跑一遍。再往前,应用共享,但每个客户有自己的数据库:不用给每个客户跑一整套技术栈,数据隔离也够强,是很常见的折中。另一端就是完全的单租户,应用和数据库都是独享的。AWS 的 SaaS 指南把两端叫作 silo(筒仓)和 pool(池),两者混用叫 bridge(桥接),向客户的安全团队解释你的架构时,这套说法挺好用。

单租户 vs 多租户一览
#

单租户(多实例)多租户(单实例)
数据隔离每个客户独立的基础设施靠代码或数据库策略保证
每个客户的成本高:每个客户一整套技术栈低:大家共用一套
运维部署、升级、监控面板都按客户数量来各一份
吵闹的邻居没有一个重度租户可能拖慢所有人
定制可按客户定制,甚至版本都能不同基本只到配置和功能开关
发布按客户逐个推所有人同时
一次糟糕的部署会影响一个客户所有客户
安全审查和数据驻留好回答要多费口舌

什么时候用哪种
#

除非有具体原因把你推开,否则就选多租户。客户多、每家付得不多、用户自己注册、团队小到三十套环境就能把人拖垮,这些情况下,多租户都是正确的默认选项。

单租户只在少数几种情况下值回它的开销。医疗、金融、政府类客户常常要求数据物理隔离,或者必须放在某个国家,而在安全审查里,独享实例是对此最简单也最老实的回答。如果只有几家签企业合同的大客户,他们负担得起自己那一套,往往也期待有一套。再就是客户需要自己的发布节奏、锁定某个版本,或者深度定制的时候,分开的实例就不再是开销,而成了你卖的产品的一部分。

大多数团队最后两种都用:绝大多数客户用多租户产品,少数需要并且愿意付钱的客户,作为企业版给独享实例。只要多租户版本从第一天起处处带着租户 ID,以后把某个客户单独迁到自己的数据库,就是一次迁移,而不是重写。

让多租户的隔离更难被打破
#

多租户里最让人睡不着的 bug,是忘了加租户过滤的查询。它不会报错,只会悄悄把别人的数据返回给你,而代码评审也不可能每次都抓到。如果你用的是 Postgres,行级安全(RLS)可以把这道检查挪到数据库里,这样就算查询忘了过滤,也只能看到当前客户的数据行:

ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON invoices
    USING (tenant_id = current_setting('app.tenant_id')::uuid);

然后每个请求在事务里用 SET LOCAL app.tenant_id = '…' 设置租户。这样连接池里的连接不会把上一个客户的 ID 带进下一个请求,忘了设置租户的请求会直接报错,而不是拿到数据。有两个坑:应用要用一个不是表所有者的角色连接,因为除非再加上 FORCE ROW LEVEL SECURITY,表的所有者会绕过这些策略;也永远不要用超级用户连接,超级用户无论如何都会绕过。

单租户从哪里开始?
#

真要走单租户,工具决定它是“可以管”还是一场慢动作灾难。三十套环境,靠手是管不过来的。

核心是容器加基础设施即代码。Docker把应用打包成对每个租户都完全一致的形态,Kubernetes负责这一堆实例的部署和扩缩容;用Terraform、Pulumi或AWS CloudFormation把一个租户的完整技术栈定义成代码,第31个客户的开通就是一个PR,而不是一个报废的周末。如果你更贴近虚拟机的世界,Ansible、Chef、Puppet这类配置管理工具可以承担同样的角色。CI/CD流水线(Jenkins、GitLab CI/CD)在这里比平时更重要,因为每次发布都要在所有实例上滚动完成,不能靠人盯。

再往后就看产品形态了。有些工作负载可以直接用无服务器(AWS Lambda、Azure Functions)把服务器这一层整个抹掉。Amazon RDS、Azure SQL Database这类托管数据库,能把你从“每个租户手动维护一个库”的苦役里救出来。有了一个集群之后,监控(Prometheus、Grafana、New Relic)就不再是可选项:哪个实例不舒服,你得赶在客户开口之前知道。身份这块交给Auth0或Okta这样的IAM服务,就不用每个租户重造一遍认证。不管你用什么语言,多半都有面向SaaS的框架或库能帮你省掉一部分这类底层活儿;托管方面,AWS、Azure、Google Cloud Platform这些大厂云都能轻松接住。

别想着第一天就把清单买齐。先上容器和IaC,租户多起来再加监控和IAM,剩下的,让真实的痛点来决定。

长话短说
#

单租户,也就是多实例,是每个客户一套独立的应用和数据库:隔离强、能按客户控制,代价是要运维一整个集群。多租户是大家共用一套:便宜、简单,隔离靠代码保证。大多数产品应该从多租户起步,处处带上租户 ID 并让数据库来强制执行,再给少数需要并愿意付钱的客户提供独享实例。如果真要跑单租户,让它保持可控的关键是容器和基础设施即代码。