多租户隔离:定义、方案、实现与选型指南
一、什么是多租户隔离
多租户(Multi-Tenant)是一种软件架构设计,指用一套或多套共享的基础设施,同时为多个相互独立的客户(租户)提供服务,通过技术手段保证各租户之间数据隔离、权限隔离、资源隔离,租户感知不到彼此的存在,使用体验等同于独占一套独立系统。
核心概念区分
- 租户:组织级主体,通常是一家企业、一个机构、一个事业部,拥有独立的业务数据、配置、权限体系
- 用户:租户内的个体账号,一个租户下可包含多个用户,用户只能访问所属租户的数据
- 隔离本质:不是物理上的多套系统,而是通过逻辑/物理规则实现「共享资源 + 权限边界」,在成本和安全性之间取得平衡
二、典型应用场景
-
ToB SaaS 产品(最核心场景)
例如企业CRM、ERP、协作办公、电商SaaS平台,每个入驻企业为一个租户,数据完全隔离,各自管理自己的员工、业务数据。 -
云服务与平台型产品
IaaS/PaaS 云平台(阿里云/腾讯云账号体系)、Kubernetes 集群多租户、开发者开放平台,每个租户独享资源配额与权限范围。 -
集团型企业内部系统
一套集团统一系统服务多个分子公司、事业部,各单元业务独立、数据隔离,同时支持集团层统一统计。注意:此场景下需明确分子公司是否需要完全数据隔离(租户模式),还是仅需权限分级(RBAC 模式)。财务独立核算的子公司通常按租户隔离,事业部可能只需 RBAC 权限控制。
-
政务/园区/产业平台
一套平台服务多个委办局、园区入驻企业,数据互不互通,满足监管合规要求。 -
教育/医疗等行业系统
多学校、多医院共用一套系统,患者/学生数据严格按机构隔离,满足行业合规要求。
三、核心技术方案(分层设计)
多租户隔离是全链路工程,不是单一方案,通常从数据层、应用层、中间件层、基础设施层四层逐级实现,其中数据层是核心,决定了隔离强度的上限。
(一)数据层隔离(三种经典方案)
数据层是多租户的核心,按照隔离强度从高到低分为三类,也是行业最通用的划分标准。
| 方案 | 实现方式 | 隔离级别 |
|---|---|---|
| 独立数据库 | 每个租户一个独立数据库实例 | 物理隔离,最高 |
| 共享数据库 + 独立 Schema | 同个数据库实例,每个租户一个独立表空间/Schema | 逻辑隔离,中等 |
| 共享数据库 + 共享 Schema + 租户ID | 所有租户共用一套表,每行数据带 tenant_id 区分 | 行级逻辑隔离,最低 |
方案1:独立数据库(Database per Tenant)
- 实现:租户开通时创建独立数据库实例,应用层通过动态数据源路由,根据请求中的租户ID切换对应数据库连接。
- 优点:
- 物理级隔离,数据安全性最高,完全满足金融、政务等强合规要求
- 单个租户故障、性能问题不会影响其他租户,故障隔离性好
- 可单独为租户做备份、迁移、扩容、定制化表结构
- 缺点:
- 硬件成本、运维成本极高,租户数量越多资源浪费越严重
- 跨租户统一统计、升级迭代难度极大,每个库都要执行脚本
- 适用场景:大客户定制、金融/医疗/政务强合规场景,租户数量少(通常<20个)
方案2:共享数据库 + 独立 Schema
-
实现:共用一个数据库实例,每个租户拥有独立的 Schema(表空间),表结构完全一致。请求时通过数据源路由切换到对应 Schema。
关于数据库支持:PostgreSQL 原生支持 Schema(同一数据库内的逻辑命名空间),可实现同一连接内跨 Schema 查询。MySQL 没有原生 Schema 支持,
CREATE SCHEMA实质是CREATE DATABASE的别名,通常用多个 Database 模拟,但跨库查询和事务管理更复杂。因此该方案在 PostgreSQL 生态中更为自然。 -
优点:
- 隔离程度中等,逻辑上表级隔离,安全性优于行级方案
- 运维成本低于独立库,数据库实例数量少,统一管理方便
- 单个租户数据迁移、备份相对简单
-
缺点:
- 同实例资源竞争,一个租户打满CPU/IO会影响全部租户
- 跨 Schema 关联查询复杂,全局统计难度高
- 表结构升级仍需遍历所有 Schema,运维量随租户数线性增长
-
适用场景:中等规模租户(几十到上百个),对隔离有一定要求但成本可控,比如SaaS产品的大客户专区
方案3:共享数据库 + 共享 Schema + 租户ID 行级隔离
- 实现:所有租户共用一套业务表,每张表增加
tenant_id字段,查询时强制追加where tenant_id = xxx过滤条件;通常通过应用层框架插件或数据库行级安全策略(RLS)自动兜底。 - 优点:
- 成本最低,一套库表支持海量租户,资源利用率最高
- 运维最简单,表结构升级、全局统计、备份都只需操作一套库
- 产品迭代效率最高,无需为每个租户单独适配
- 缺点:
- 隔离级别最低,代码一旦漏写租户条件,直接造成跨租户数据泄露
- 头部大租户数据量膨胀会导致单表性能瓶颈
- 单个租户数据导出、迁移、物理删除成本高
- 适用场景:标准化SaaS产品、海量中小租户、单租户数据量不大、追求规模化与成本最优,是绝大多数通用SaaS的主流方案
增强实现:PostgreSQL 行级安全(RLS)
共享 Schema 方案的兜底神器,数据库层面强制行过滤,即使应用层SQL漏写租户条件,数据库也会自动追加tenant_id过滤,从根源避免数据泄露,是PG生态多租户的标配。
(二)应用层隔离
数据层是基础,应用层是隔离的第一道防线,覆盖全链路:
-
租户身份识别
- 子域名隔离:
tenant-a.xxx.com,网关解析子域名得到租户ID - 请求头/Token传递:JWT 登录态携带租户ID,接口统一从上下文读取,禁止前端直接传租户ID
例外说明:超级管理员/运营后台需要跨租户操作时,可通过特殊权限接口传递目标租户 ID,但需经过严格的后端权限校验和操作审计。
- 子域名隔离:
-
租户上下文透传
网关/拦截器解析租户ID后,存入请求上下文(ThreadLocal、RPC 上下文),全服务链路自动传递,无需业务代码手动传参。 -
业务权限隔离
租户级的菜单、角色、配置、计费配额全部按租户维度隔离,支持租户自定义权限体系。 -
接口兜底校验
全局拦截器统一校验数据归属,防止越权访问其他租户数据。
(三)中间件层隔离
仅隔离数据库远远不够,缓存、消息队列、文件存储同样需要租户隔离,是高频踩坑点。
-
缓存隔离
- 轻量方案(推荐):Key 拼接租户前缀,如
tenant:1001:user:123,共享 Redis 实例(兼容性最好,Cluster 模式也支持) - 强隔离:大客户独占 Redis 实例
- 不推荐:Redis 多库号隔离(db index),因为 Redis Cluster 模式下不支持多 db,且无法做细粒度资源监控
- 轻量方案(推荐):Key 拼接租户前缀,如
-
消息队列隔离
- 轻量方案:消息体携带租户ID,消费端校验过滤;Topic 增加租户前缀
- 强隔离:大客户独占 Topic 和消费组
-
文件/对象存储隔离
按租户ID分目录/分桶存储,访问时校验租户权限。
(四)基础设施层隔离
面向中大型租户的资源隔离,通常配合K8s、云平台实现:
- 计算隔离:K8s Namespace 隔离、节点亲和性,大客户独占物理节点
- 网络隔离:VPC、子网、安全组按租户划分,网络层不可互通
- 资源配额:CPU、内存、带宽按租户限流,防止单租户抢占全部资源
四、主流方案优缺点横向对比
| 对比维度 | 独立数据库 | 共享库+独立Schema | 共享库+租户ID行级 |
|---|---|---|---|
| 数据隔离强度 | 物理级,最高 | 逻辑表级,中等 | 行级逻辑,最低 |
| 故障隔离性 | 完全隔离,互不影响 | 同实例资源竞争,互相影响 | 完全共享,互相影响 |
| 硬件成本 | 极高 | 中等 | 最低 |
| 运维复杂度 | 极高,多实例管理 | 中等,多Schema管理 | 最低,单库维护 |
| 单租户数据迁移 | 极简单,直接备份库 | 较简单,导出Schema | 复杂,需过滤导出 |
| 跨租户统计分析 | 极难,需跨库聚合 | 较难,需跨Schema | 最简单,单库直接统计 |
| 定制化支持度 | 极高,可单独改表结构 | 中等,表结构可独立 | 低,必须统一表结构 |
| 租户数量上限 | 几十个 | 几百个 | 上万甚至几十万 |
| 数据泄露风险 | 极低 | 较低 | 较高,依赖代码规范 |
| 性能影响参考 | 无性能干扰,但连接数随租户线性增长 | 同实例资源竞争,大查询可能影响其他租户 | tenant_id 索引命中时接近独立库;索引缺失或数据倾斜时性能急剧下降 |
五、标准落地实现步骤(行级隔离方案,最通用)
以「PostgreSQL + Java 后端 + 网关」的典型架构为例:
1. 数据层设计(双重保障)
- 所有业务表新增
tenant_id字段,与主键建立联合索引,优化查询性能 - 开启 PostgreSQL 行级安全策略(RLS),数据库层面兜底过滤
-- 开启行级安全
ALTER TABLE tenants ENABLE ROW LEVEL SECURITY;
-- 创建隔离策略,强制匹配当前会话的租户ID
CREATE POLICY tenant_isolation_policy ON tenants
FOR ALL USING (tenant_id = current_setting('app.current_tenant')::bigint);
重要前置步骤:使用
current_setting前,必须在每次获取数据库连接后立即设置会话级参数,否则会报错:SET app.current_tenant = '123'; -- 123为当前租户ID应用层可在连接池获取连接后、执行业务SQL前,通过 AOP 拦截器统一执行
SET语句,RLS 策略会自动生效。
- 应用层连接池每次请求前设置会话级租户ID,RLS自动生效
2. 框架层自动注入(杜绝手动写漏)
使用 MyBatis 插件 / JPA 过滤器 / ORM 租户插件,在SQL执行前自动追加 tenant_id 条件,业务代码完全无感知。
核心原则:绝对不允许业务开发手动在SQL里写
tenant_id = xxx,必须框架层统一注入,从流程上避免遗漏。
3. 网关与接口层
- 统一网关从 JWT 登录态解析租户ID,校验租户状态(是否停用、到期)
- 将租户ID写入请求上下文,向下游服务透传
- 禁止前端直接传递租户ID,所有租户身份以服务端登录态为准
4. 缓存与中间件
封装统一缓存工具类,自动为所有 Key 拼接租户前缀;MQ 消费时统一校验租户归属。
5. 租户管理中心
独立租户服务,负责:
- 租户创建、启停、到期、配额配置
- 租户数据初始化、备份、注销
- 租户级限流、计费统计
6. 隔离测试验证
编写自动化测试用例,模拟租户 A 的 Token 访问租户 B 的数据,验证返回 403 或空结果。建议纳入 CI/CD 流水线,防止回归。
六、选型方法与决策依据
核心选型维度
- 合规安全要求:金融/政务/医疗等强监管行业,优先独立数据库;普通商业SaaS可选行级或Schema级
- 租户规模与结构:少量大客户 → 独立库;中等数量客户 → Schema级;海量中小租户 → 行级共享
- 单租户数据量:单租户单表数据量超百万级且持续增长 → 优先高隔离方案;单租户数据量小 → 行级
- 定制化需求:租户需要大量表结构、业务逻辑定制 → 独立库/Schema;标准化产品无定制 → 行级
- 运维与成本预算:预算充足、运维团队完善 → 高隔离;追求极致成本、快速规模化 → 行级共享
- 跨租户运营需求:需要频繁全局统计、统一运营分析 → 优先共享库方案
主流实践:混合多租户架构
中大型SaaS的最优解不是单一方案,而是混合架构:
- 90% 中小租户:共享库 + 行级隔离,低成本规模化
- 10% 头部大客户/合规客户:独立数据库(共享应用实例,数据库物理隔离)或 独立部署(应用 + 数据库完全独立,满足最高合规要求,但成本翻数倍)
明确区分:“独立数据库”指共享应用实例、仅数据库物理隔离;“独立部署”指为租户部署一套完全独立的系统,成本高出数倍,仅用于最高合规场景。
兼顾成本与大客户需求,是行业最主流的落地模式。
选型决策树
是否有强等保/行业合规要求?
├── 是 → 租户数量是否较少(<50)?
│ ├── 是 → 独立数据库 + 基础设施层物理隔离
│ └── 否 → 独立数据库集群 + 自动化运维平台
│ (成本极高,需评估业务可行性,或考虑合规豁免)
└── 否 → 租户数量是否少于50且单租户数据量大?
├── 是 → 共享数据库 + 独立Schema
└── 否 → 标准化SaaS、海量中小租户 → 共享库共享Schema + RLS兜底 + 应用层隔离
└── 存在头部大客户 → 叠加混合架构,大客户单独隔离
七、常见坑与最佳实践
-
数据泄露风险兜底:不要只靠应用层控制,数据库层必须加RLS或同类兜底机制,代码总有写错的时候,数据库层是最后一道防线。
-
禁止前端传租户ID:租户身份必须从服务端登录态解析,前端传参只做辅助校验,防止篡改越权。
-
全链路隔离:不能只隔离数据库,缓存、MQ、文件存储、日志都要做租户维度隔离,缓存跨租户泄露是高频事故点。
-
大租户限流隔离:必须做租户级限流、资源配额,防止单个大租户的流量打满全站资源,影响所有租户。
-
避免跨租户业务查询:运营统计、数据分析走独立数仓,不要在业务库做跨租户聚合,拖垮线上性能。
具体做法:通过 CDC(Change Data Capture)工具(如 Debezium)将业务库数据实时同步到 ClickHouse / StarRocks 等分析型数据库,在分析库中做跨租户聚合,不影响在线业务。
-
数据生命周期管理:支持单租户数据完整导出、物理删除,满足《个人信息保护法》等合规要求。
-
隔离测试自动化:将跨租户越权测试纳入 CI/CD 流水线,防止代码变更引入隔离漏洞。

912

被折叠的 条评论
为什么被折叠?



