一个 SaaS 系统开始只有几十个客户时,数据库架构往往看起来很简单。所有企业共用一套数据库,业务表里增加一个 tenant_id,然后每次查询时带上租户条件,系统就能正常运行。
问题通常出现在客户数量、数据规模和业务复杂度不断增加以后。
当一个 SaaS 平台拥有几千甚至几万家企业客户时,数据库究竟应该继续让所有租户共享表,还是给每个租户独立 Schema,甚至独立数据库?这个问题并不存在一个适合所有公司的标准答案。数据库隔离程度越高,通常意味着更强的故障和数据隔离能力,但同时也会增加基础设施、迁移、备份、监控和运维成本。
因此,多租户数据库设计的核心并不是“哪一种架构最安全”,而是在数据隔离、性能、扩展能力、成本和运维复杂度之间找到合适的平衡。
最常见、也是很多 SaaS 产品早期最实用的方案,是共享数据库、共享表。
例如系统中存在 users、projects、orders、documents 等业务表,每一条数据都包含 tenant_id:
id
tenant_id
name
created_at
企业 A 的数据使用 tenant_id=1001,企业 B 使用 tenant_id=1002。所有客户的数据实际上都存放在相同的数据库和相同的表中。
这种方案最大的优势是简单。
数据库只需要维护一套表结构,应用程序也只需要维护一套数据库连接。新增租户基本不需要创建数据库对象,数据库迁移也只需要执行一次。对于客户数量很多、但数据模型高度统一的 SaaS 产品来说,这种架构非常有吸引力。
例如项目管理、CRM、客服系统、在线表单、内容管理、协作平台等,如果不同企业使用的核心业务模型基本一致,共享表通常是非常合理的起点。
但它最大的风险也非常明确:租户隔离必须由应用程序和数据库访问层严格保证。
假设用户属于企业 A,而程序执行:
SELECT * FROM orders WHERE id = 123
却没有加入 tenant_id 条件,那么只要订单编号恰好属于企业 B,应用程序就可能把 B 企业的数据返回给 A 企业。
因此,多租户系统最危险的错误,往往不是数据库结构本身,而是某一个查询忘记了租户边界。
比较可靠的做法,是让租户上下文进入数据访问层,而不是依靠开发人员每次手写条件。例如所有租户数据查询都必须经过统一的 Repository、ORM Scope、数据库策略或者其他访问层机制,在执行查询时自动加入 tenant_id 条件。
如果使用 PostgreSQL,还可以进一步利用 Row-Level Security,也就是行级安全策略,让数据库本身参与租户隔离。这样即使应用程序某个查询遗漏了租户条件,数据库层仍然可以根据当前会话的租户上下文限制能够读取的数据范围。
共享表架构还有一个非常重要的问题:索引设计。
如果一张 orders 表有几千万甚至上亿条记录,而绝大多数查询都是“某个租户最近的订单”,那么单独给 tenant_id 建索引只是一个起点。很多情况下,更合理的索引可能是:
INDEX (tenant_id, created_at)
如果经常按照用户查询,则可能需要:
INDEX (tenant_id, user_id, created_at)
也就是说,多租户系统的索引设计必须考虑“租户条件”本身,而不能完全按照单租户应用的思路建立索引。
随着规模进一步扩大,另一种方案开始出现:共享数据库,但每个租户使用独立 Schema。
这种架构下,数据库实例可以是同一个,但不同租户的数据分别放在不同 Schema 中。例如:
tenant_a.orders
tenant_b.orders
tenant_c.orders
这样做的好处,是租户之间的数据库对象边界更加明显。不同租户的数据不再全部堆在同一张业务表里,因此某些数据迁移、备份或者定制化需求会更容易处理。
但它也带来一个非常现实的问题:租户数量增加以后,数据库对象数量也会迅速增加。
如果平台有10万个租户,那么是不是需要维护10万个 Schema?每次数据库升级是不是要对10万个 Schema 执行迁移?一个新字段加入订单表时,迁移脚本如何保证所有租户都完成更新?
这些问题会让原本简单的数据库迁移体系逐渐复杂化。
因此,独立 Schema 并不是“共享表的加强版”,而是一种在隔离和运维成本之间做出的不同取舍。对于租户数量相对可控、数据隔离要求高于普通共享表、同时又不希望维护大量独立数据库的 SaaS 系统,它可能比较合适。
再往上一级,就是每个租户独立数据库。
例如平台拥有企业 A、企业 B、企业 C,那么数据库层面可能分别存在:
tenant_a_db
tenant_b_db
tenant_c_db
应用程序根据当前租户选择对应的数据库连接。
这种方案提供了非常清晰的数据边界。一个租户的数据、索引、表结构、备份和数据库资源都可以独立管理。如果某个大型客户要求单独备份、单独恢复、独立数据保留周期,甚至要求数据存储在指定区域,独立数据库会更加灵活。
对于医疗、金融、法律、政府供应商以及大型企业客户等对数据隔离有特殊要求的场景,这种设计具有明显优势。
但代价同样非常明显。
数据库数量增加以后,备份、监控、升级、迁移、连接池、故障处理和权限管理都会变得更加复杂。假设不是100个租户,而是10万个租户,那么“每个租户一个数据库”很可能会把数据库运维变成系统本身的一大负担。
因此,“每租户独立数据库”并不意味着安全性和可靠性自动达到最高水平,也不意味着大型 SaaS 就一定应该这么设计。它更适合那些租户数量有限、单个租户价值较高、隔离要求严格或者存在大量客户定制需求的场景。
现实中的大型 SaaS 更常见的做法,是混合架构。
例如普通客户使用共享数据库和共享表,大型企业客户使用独立数据库;普通业务数据共享存储,而对敏感数据采用更加严格的隔离方式;日志、统计数据和分析数据则可能进入独立的数据仓库或日志系统。
甚至同一个 SaaS 平台内部,也不一定所有数据都使用同一种数据库架构。
例如用户和基础配置数据可以使用共享表,企业核心业务数据根据客户等级分配到不同数据库,审计日志进入独立存储系统,分析数据进入数据仓库。
这实际上形成了一种“分层多租户”架构。
这种设计最大的优势,是资源可以按照客户价值和风险进行分配。
一个只有5名员工的小企业,没有必要占用一整套独立数据库。但一家拥有几万名员工、每天产生数百万条业务记录的大型企业,可能值得拥有专用数据库资源。
这样既能控制整体成本,又能避免少数超大型租户把共享数据库拖慢。
数据库架构还必须考虑一个经常被低估的问题:大租户和小租户之间的资源竞争。
假设共享数据库中有5000个企业,其中一个客户突然开始批量导入几千万条记录。如果数据库没有做好索引、限流、队列、连接池和资源隔离,那么这个客户的行为可能影响其他几千家企业。
这就是所谓的“嘈杂邻居”问题。
解决办法不一定是立刻给所有租户建立独立数据库。可以通过数据库分区、异步任务、队列、限流、缓存、读写分离、连接池控制以及大型租户迁移等方式逐步解决。
数据量特别大的情况下,还可以考虑分区表。例如按照时间或者租户范围进行分区,让数据库减少无关数据扫描。不过分区并不会自动解决租户隔离问题,它主要是性能和数据管理手段。
安全方面同样不能把“tenant_id”当成完整的安全方案。
多租户安全至少包括身份认证、成员关系、权限控制、租户边界、数据库访问控制、加密、密钥管理、审计日志以及备份保护。
尤其是在 SaaS 系统中,一个用户可能属于多个企业。
因此系统不能简单把 users 表设计成:
user_id
organization_id
因为同一个用户可能同时属于企业 A 和企业 B。
更加合理的模型通常是 users、organizations 和 memberships 分开。
membership 保存:
user_id
organization_id
role_id
status
这样,同一个用户可以在企业 A 中是管理员,在企业 B 中只是普通成员。
用户当前正在操作哪个企业,也应该成为明确的租户上下文。服务器必须重新验证用户是否属于该企业,而不能直接相信浏览器提交的 organization_id 或 tenant_id。
这件事情与数据库架构同样重要。
因为即使数据库采用最先进的隔离方案,如果应用程序允许用户任意切换租户上下文,系统仍然可能产生越权访问。
数据迁移也是选择架构时必须提前考虑的问题。
共享表最大的优势是迁移一次即可影响所有租户;独立 Schema 或独立数据库则可能需要逐个处理。
因此,SaaS 在设计数据库时不能只问“现在有多少客户”,还应该问“未来需要支持多少客户”“最大的单个租户会有多少数据”“是否允许大型客户单独部署”“客户是否要求数据驻留某个国家或地区”“能不能接受租户之间共享基础设施”。
如果这些问题没有提前考虑,系统早期可能非常漂亮,客户规模一旦增长,数据库架构却会成为最大的技术债务。
对于刚开始开发的 SaaS 产品,大多数情况下没有必要一上来就建立极其复杂的数据库集群。共享数据库、共享表,加上严格的 tenant_id 隔离、合理索引和完善的权限模型,往往已经足够支撑早期业务。
当数据规模、客户价值和合规要求不断提高,再把部分大型租户迁移到独立数据库,也是一条现实的演进路线。
因此,多租户数据库真正需要设计的并不是一张简单的“架构选择表”,而是一条能够随着业务成长不断调整的路线。
共享表强调成本和规模效率,独立 Schema 提供更清晰的数据边界,而独立数据库提供更强的租户级资源和运维隔离。混合架构则把这些方案组合起来,根据客户规模、数据敏感程度和业务特点进行分配。
对于 SaaS 来说,最理想的数据库架构通常不是从第一天开始就最复杂,而是能够在客户从几十家增长到几千家、几万家之后,依然有办法平稳演进。数据库不是孤立存在的技术组件,它最终服务的是租户模型、权限模型、业务规模和公司的商业模式。把这些边界在最初设计清楚,未来很多数据库问题就不会变成只能靠停机重构来解决的大麻烦。
SaaS 多租户数据库有哪些常见设计方式,各自适合什么场景
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
分享 Facebook | X | WhatsApp | LinkedIn
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP