一个 SaaS 产品发展到一定规模后,数据库架构几乎一定会遇到一个问题:到底应该让每个客户拥有自己的数据库,还是让所有客户共用一套数据库?
这个问题表面上是在选择数据库部署方式,实际上是在决定整个 SaaS 系统的数据隔离、扩展、备份、升级和运维方式。
假设一个系统有一百家公司使用,每家公司都有自己的用户、订单、项目和文件。如果把一家公司理解成一个“租户”,那么数据库至少有四种常见设计:所有租户共用数据库和数据表;所有租户共用数据库但使用不同 Schema;每个租户使用独立数据库;以及根据客户规模和数据敏感程度采用混合架构。
没有一种方案天然适合所有 SaaS。
真正合理的设计,是在隔离程度、成本、性能、运维复杂度以及未来扩展之间找到一个能够长期承受的平衡。
最常见的共享方案是所有租户共用数据表
对于大量标准化 SaaS 产品来说,一个数据库加一套共享表,是非常常见的设计。
例如:
users
organizations
projects
orders
invoices
每一张需要租户隔离的业务表增加:
organization_id
于是数据库中可能同时存在:
organization_id = 101
organization_id = 102
organization_id = 103
的数据。
应用程序查询时必须始终带上租户条件:
SELECT *
FROM projects
WHERE organization_id = 101;
这里最重要的不是这一条 SQL,而是整个系统必须建立“租户上下文”。
用户登录以后,系统知道他属于哪些组织,以及当前正在操作哪个组织。之后每一次访问项目、订单、客户、账单等数据时,服务器都必须验证用户是否拥有对应组织的访问权限。
绝不能因为浏览器提交:
organization_id=101
就直接相信这个值。
否则攻击者只需要把 101 改成 102,就可能尝试访问另一个客户的数据。
因此,共享表架构最大的风险不是“数据库放在一起”,而是应用程序是否能够始终正确执行租户隔离。
用户、企业和成员关系必须分开设计
多租户 SaaS 一个非常容易犯的错误,是在 users 表里面直接放一个:
organization_id
然后假设一个用户只能属于一家企业。
现实中的 SaaS 往往不是这样。
同一个用户可能同时属于:
公司 A:管理员
公司 B:普通成员
公司 C:财务人员
因此更合理的结构是:
users
organizations
memberships
roles
其中 memberships 表负责描述:
user_id
organization_id
role_id
status
这样用户身份和企业身份就不会被绑死。
这个设计对于数据库架构选择同样重要,因为真正需要隔离的并不是“用户”,而是租户的数据和权限边界。
共享数据库并不等于共享所有数据
第二种方案是共享一个数据库,但每个租户使用独立 Schema。
例如 PostgreSQL 中可以设计:
tenant_a.projects
tenant_a.orders
tenant_b.projects
tenant_b.orders
这样不同租户之间的数据库对象可以进一步隔离。
这种方式比所有租户共用同一套表具有更明显的边界,但同时也带来了更多数据库对象。
假设 SaaS 有一万个租户,那么数据库中可能出现大量 Schema、表、索引以及迁移任务。
数据库升级就不再只是执行一次 SQL,而可能变成需要管理大量租户对象。
因此,Schema-per-tenant 并不是简单意义上的“更安全版本”,而是一种用更高运维复杂度换取更强逻辑隔离的设计。
每个客户一个数据库到底意味着什么
如果采用独立数据库,那么可能变成:
Tenant A → Database A
Tenant B → Database B
Tenant C → Database C
甚至进一步做到:
Tenant A → 独立数据库服务器
Tenant B → 独立数据库服务器
这两种隔离程度并不相同。
“每个客户一个数据库”不一定意味着“每个客户一台数据库服务器”。
例如 MySQL、PostgreSQL 等数据库系统完全可以在同一套基础设施上运行多个数据库。
独立数据库的主要优势,是故障、备份、恢复和权限边界更加容易针对单个租户进行管理。
例如某个大型客户需要:
独立备份
独立恢复
独立数据迁移
独立数据库参数
独立数据驻留区域
独立数据库就具有很大的价值。
如果客户要求自己的数据必须放在特定国家或地区,或者希望拥有独立的恢复策略,那么把该客户从共享数据库中隔离出来也可能更加合理。
分库最大的代价不是磁盘,而是运维
原稿把“每个客户需要独立存储空间,因此存储利用率低”作为主要问题,其实并不是最核心的矛盾。
现代数据库存储本身并没有那么简单的“一个客户占一块硬盘”的关系。
真正让独立数据库变贵的地方,是管理对象数量增加。
例如原来只有一个数据库:
1 套数据库
1 套备份策略
1 套监控
1 套升级流程
如果变成一千个数据库,就需要考虑:
1000 个数据库
1000 套备份任务
1000 个监控对象
1000 个恢复目标
大量连接管理
大量版本迁移
大量故障处理
当然,实际部署可以通过自动化平台、数据库集群和基础设施即代码降低管理成本,但系统的运营复杂度仍然明显增加。
因此,分库真正的成本是“规模化运维”。
分库的一个巨大优势是控制单个租户的资源
共享数据库最容易出现的一种问题叫“嘈杂邻居”。
假设一个普通 SaaS 有五百家公司。
其中一家突然执行了一个非常大的报表查询:
扫描数千万行数据
大量排序
大量聚合
持续占用 CPU 和磁盘 I/O
那么其他租户可能也会受到影响。
这种问题并不是说共享数据库一定性能差,而是共享资源意味着需要认真进行资源隔离。
可以通过:
索引优化
查询限制
连接池
缓存
队列
异步任务
数据库资源控制
读写分离
分区
分片
等手段降低影响。
如果某个大型客户已经成为整个系统的“超级租户”,那么把它迁移到独立数据库往往比不断优化共享数据库更加简单。
共享数据库并不意味着只能垂直扩展
这是原稿中另一个需要纠正的地方。
共享数据库当然可以进行水平扩展。
例如 PostgreSQL 可以通过读副本、分区以及其他架构手段扩展;MySQL 也可以通过读写分离、分片等方式扩大系统容量。
更进一步,还可以按照租户进行数据库分片:
Tenant 1–1000
↓
Database Shard A
Tenant 1001–2000
↓
Database Shard B
Tenant 2001–3000
↓
Database Shard C
这样系统仍然采用共享表模型,但不再把所有租户放进同一个数据库实例。
这是一种非常重要的演进路线。
因此,“共享数据库”和“不能水平扩展”并不是一回事。
金融和医疗是不是一定要每个客户分库
也不能简单这么判断。
金融、医疗等行业的数据确实可能具有更高的隐私、安全、审计和合规要求,但“必须独立数据库”并不是一个放之四海而皆准的技术结论。
有些系统完全可以使用共享数据库,同时通过严格的访问控制、租户隔离、加密、审计日志以及数据库安全机制满足要求。
另一方面,有些客户可能因为合同、监管、数据驻留或者内部安全政策,明确要求物理或逻辑上的更强隔离。
所以正确的决策依据应该是:
监管要求
合同要求
数据敏感程度
恢复要求
数据驻留要求
客户规模
性能要求
安全模型
运营能力
而不是简单地看到“医疗”两个字就决定“一客户一数据库”。
跨租户分析也不意味着必须共享数据库
很多 SaaS 需要统一统计数据,例如:
平台活跃用户
销售趋势
产品使用情况
系统运行指标
原稿认为需要跨租户分析就必须选择共享数据库,这也过于绝对。
更成熟的系统通常不会让生产数据库直接承担所有跨租户分析任务。
可以建立:
业务数据库
↓
数据同步
↓
数据仓库 / 分析数据库
↓
报表与 BI
这样在线交易数据库负责业务,分析系统负责大规模统计。
即使每个客户使用独立数据库,也可以把经过权限控制的数据汇总到统一的数据平台。
Redis 和缓存同样必须考虑租户隔离
多租户设计不能只考虑数据库。
例如 Redis 中缓存:
user:123
project:456
如果这些键没有包含租户上下文,设计不严谨时也可能产生跨租户数据混淆。
更加安全的方式通常是让缓存键具有明确的租户范围,例如:
org:101:project:456
org:102:project:456
文件存储、搜索引擎、消息队列、日志系统也存在类似问题。
也就是说,多租户不是数据库的一项功能,而是贯穿整个系统的数据隔离原则。
最实用的方案往往是混合架构
真正成熟的 SaaS 很少把世界简单分成“全部共享”和“全部分库”。
一种非常实用的设计是:
普通客户
↓
共享数据库
大型客户
↓
独立数据库
高敏感客户
↓
独立数据库或独立基础设施
应用程序则通过统一的数据访问层决定应该访问哪个数据库。
例如:
Tenant 101 → Shared DB
Tenant 102 → Shared DB
Tenant 103 → Shared DB
Tenant 9001 → Dedicated DB
Tenant 9002 → Dedicated DB
对用户,他们看到的仍然是同一个 SaaS 产品。
对后台,不同租户可以拥有不同的数据隔离等级。
这种设计还有一个很大的好处:系统不需要在第一天就为所有客户付出最高级别的基础设施成本。
一个刚上线的 SaaS 如果只有几十家公司,却为每家公司部署独立数据库,可能很快就会发现自己不是在开发产品,而是在维护数据库。
一个好的 SaaS 应该允许数据库架构随着客户成长
比较成熟的架构甚至应该允许租户迁移。
例如最初:
所有客户
↓
共享数据库
某个客户发展到非常大的规模以后:
共享数据库
↓
Tenant 500
↓
独立数据库
再进一步,可能变成:
Tenant 500
↓
独立数据库
↓
独立计算资源
↓
独立备份策略
这意味着系统设计时最好不要把数据库连接、租户识别和业务代码写死在一起。
应用层应该知道:
当前用户是谁
当前组织是谁
这个组织的数据应该去哪一个数据库
数据库路由则可以由独立的数据访问层或者租户路由层负责。
这样以后从共享数据库迁移到独立数据库,就不会变成整个应用重写。
到底应该怎么选
如果 SaaS 刚开始开发,客户数量还不多,而且业务高度标准化,共享数据库加 organization_id 往往是非常现实的起点。
如果租户数量很多,希望降低基础设施和运维成本,也通常会优先考虑共享数据库。
如果少数大型客户拥有巨量数据,需要独立备份、恢复、性能或者数据驻留策略,可以考虑独立数据库。
如果客户对隔离要求非常高,或者合同和监管明确要求独立环境,则可以进一步提高隔离等级。
如果客户规模差异非常大,混合架构通常最有吸引力。
可以把整个选择理解成一条连续的路线:
共享表
↓
独立 Schema
↓
独立数据库
↓
独立数据库集群
↓
独立基础设施
隔离程度越高,通常意味着成本和运维复杂度也越高,但这并不意味着越贵就越好。
SaaS 数据库设计最容易犯的错误,是把“安全”和“分库”画上等号,或者把“共享数据库”和“低端架构”画上等号。实际上,真正重要的是系统能不能建立清晰而可靠的租户边界,并且让这种边界贯穿身份认证、权限、数据库查询、缓存、文件、搜索、消息和日志。
对于绝大多数早期 SaaS,最值得优先建立的并不是“一客户一数据库”,而是一个正确的多租户模型:User、Organization、Membership、Role 和业务数据各自承担清晰的职责,所有租户数据访问都经过服务器端授权。等到客户规模、性能需求、合规要求真正出现差异以后,再把部分租户迁移到独立数据库。这样既不会在产品初期背上沉重的运维成本,又保留了未来向独立数据库甚至独立基础设施演进的空间。好的 SaaS 架构不是一开始就选择最复杂的方案,而是让系统在客户数量和业务规模不断增长以后,仍然能够从一种数据隔离模式平滑地走向另一种模式。
数据库按客户分开,还是所有客户共用一套数据库?SaaS 应该如何选择
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
分享 Facebook | X | WhatsApp | LinkedIn
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP