结论先行:三种隔离方案没有绝对优劣,按租户数量、数据敏感度与预算选择:绝大多数 SaaS 从"共享库+租户字段"起步(成本最低、运维最简),单租户数据量巨大或强合规客户再升级到独立 Schema 或独立库。隔离方案真正的难点不在选型,而在租户上下文(Tenant Context)注入——一处遗漏就是一次数据泄露事故。
把一套低代码平台卖给多家企业用(SaaS 化),第一个架构问题就是:A 公司的数据怎么保证 B 公司永远看不到?本文面向正在做多租户改造的开发者与架构师,把三种方案的账算清楚。
三种隔离方案对比
| 维度 | 共享库共享表 | 独立 Schema | 独立数据库 |
|---|---|---|---|
| 做法 | 所有表加 tenant_id 字段 | 每租户一个数据库 Schema | 每租户一个数据库实例 |
| 隔离性 | 逻辑隔离,靠代码保证 | 中等,Schema 级隔离 | 物理隔离,最强 |
| 成本 | 最低,一套资源服务所有租户 | 中等 | 最高,资源随租户线性增长 |
| 运维复杂度 | 低:备份、升级一次搞定 | 中:跨 Schema 批量变更 | 高:逐实例巡检与升级 |
| 适合规模 | 租户 1000 以内,数据量可控 | 中型租户,合规要求中等 | 金融、政企等强隔离要求 |
三个方案可以混用:平台主体用共享表,大客户单独给独立库,这是成熟 SaaS 产品的常见形态。把三种隔离方案放进平台整体部署形态里看,对应的正是共享集群、独立实例与私有化三种架构。
怎么选:看三个变量
第一看租户数量。几十个大客户与上万个小微租户是完全不同的架构:租户越多,越要共享资源摊薄成本;大客户越重要,越倾向物理隔离。
第二看合规要求。客户合同里出现"数据物理隔离""独立数据库实例"字样,就只能走独立库方案;没有硬性要求的,共享表足够。
第三看数据量分布。个别租户单表过千万行时,共享表会遇到互扰问题(一个租户的慢查询拖垮全平台),把该租户迁到独立库是常规操作。
租户上下文注入的 4 个关键位置
共享表方案的安全性命脉是:所有 SQL 都自动带上 tenant_id 过滤。租户上下文需要在 4 个位置正确注入:
- 登录认证层:用户登录后,把租户 ID 写入 token(如 JWT 的自定义字段),后续请求自动携带。
- 请求入口层:网关或中间件统一解析 token,把租户 ID 放进请求上下文(ThreadLocal / Context),杜绝业务代码手工传参的遗漏。
- 数据访问层:ORM 拦截器或统一 DAO 自动为 SELECT/UPDATE/DELETE 拼接 tenant_id 条件、为 INSERT 自动填充租户字段——这是最关键的一道闸门,宁可在这里做得重一点。
- 异步任务层:定时任务、消息队列消费者没有请求上下文,必须显式传入租户 ID,这是最常被遗忘的位置。
以猫拽低代码(CatPull)为例,其开源(Apache-2.0)架构允许团队在数据模型与工作流引擎层做这类改造:给业务模型统一加租户字段、在云函数与工作流节点执行时透传租户上下文,即可把单租户平台改造成多租户底座。
常见事故与防护
- 越权查询:接口按 ID 查详情时忘记带租户过滤,遍历 ID 就能摸到别家数据。防护:数据访问层强制注入,而非依赖每个接口自觉。
- 导出遗漏:报表导出功能直连数据库写 SQL,绕过了 ORM 拦截器。防护:导出功能统一收口到带租户过滤的数据服务。
- 缓存串数据:缓存 key 没带租户 ID,A 租户读到 B 租户的缓存。防护:缓存 key 强制包含租户前缀。
- 后台任务串租户:给全租户跑批时上下文错乱。防护:任务队列消息体里显式携带租户 ID,消费端二次校验。
FAQs
Q1:一开始用共享表,以后能平滑迁到独立库吗?
A:可以,前提是数据访问层从一开始就严格按租户隔离实现。迁移时按租户 ID 导出全量数据到新库即可,主要成本在数据校验与切换窗口。建议第一张表设计时就加好 tenant_id,为未来留路。
Q2:租户 ID 放在 URL 里安全吗?
A:不安全。租户标识应从服务端会话或 token 中解析,不能信任前端传参——前端可伪造。URL 里出现租户 ID 只能作为展示用途,鉴权必须以服务端解析结果为准。
Q3:独立数据库方案的成本高多少?
A:资源成本随租户数线性增长,通常在租户过百后变得显著;更大的成本是运维——每新增一个租户都涉及建库、账号、备份策略配置。多数团队用"共享表为主+大客户独立库"的混合方案平衡成本与隔离。
延伸阅读
- 低代码应用安全加固清单:鉴权、防注入与审计——多租户越权是安全加固的重点检查项。
- 用户权限系统设计——租户隔离与权限体系是互补的两层防线。
- 低代码平台架构对比——架构是否预留多租户扩展点,选型时值得深挖。
猫拽智库