2. 租户隔离模型:共享数据库共享Schema、共享数据库独立Schema、独立数据库模式对比
在多租户SaaS架构中,数据隔离模型是决定系统安全性、扩展性与运维成本的核心决策。本章深入对比三种主流隔离模式:共享数据库共享Schema、共享数据库独立Schema、独立数据库模式,帮助你在不同业务场景下做出最优选择。
2.1 三种隔离模式概述
三种模式在数据隔离粒度、资源利用率、运维复杂度上存在本质差异。下表从关键维度进行横向对比:
| 对比维度 | 共享数据库共享Schema | 共享数据库独立Schema | 独立数据库模式 |
|---|---|---|---|
| 隔离粒度 | 行级(tenant_id字段) | 表级(Schema命名空间) | 数据库级(物理实例) |
| 资源利用率 | 最高(完全共享) | 中等(共享实例) | 最低(独占资源) |
| 数据安全性 | 最低(依赖应用层过滤) | 中等(Schema天然隔离) | 最高(物理隔离) |
| 租户扩展上限 | 极高(单实例支持数万租户) | 较高(单实例支持数千租户) | 较低(受实例数量限制) |
| 备份恢复粒度 | 全库备份,无法单独恢复某租户 | 可单独备份/恢复某个Schema | 可单独备份/恢复整个数据库 |
| 运维复杂度 | 低(统一管理) | 中等(需管理多个Schema) | 高(需管理多个实例) |
| 典型适用场景 | 中小型SaaS、免费/低客单价产品 | 中大型SaaS、行业垂直应用 | 金融、医疗、政府等高合规行业 |
2.2 模式一:共享数据库共享Schema
所有租户的数据存储在同一个数据库的同一组表中,通过tenant_id字段区分数据归属。这是资源利用率最高的模式,也是安全风险最大的模式。
⚠️ 安全红线:共享Schema模式下,所有SQL查询必须强制携带tenant_id过滤条件。任何应用层鉴权漏洞都可能导致租户数据越权访问。建议配合数据库行级安全策略(RLS)做双重保障。
2.2.1 核心实现要点
- 数据表设计:每张业务表必须包含
tenant_id列,并建立联合索引。 - 查询拦截:在ORM层或数据库中间件层自动注入
WHERE tenant_id = ?条件。 - 连接池管理:所有租户共享同一个数据库连接池,无需为租户单独创建连接。
-- 示例:共享Schema下的订单表结构
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
tenant_id VARCHAR(64) NOT NULL, -- 租户标识
order_no VARCHAR(64) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
created_at DATETIME NOT NULL,
INDEX idx_tenant_order (tenant_id, created_at)
);
-- 每次查询必须携带tenant_id
SELECT * FROM orders WHERE tenant_id = 'tenant_abc123' AND order_no = 'ORD2024001';
2.3 模式二:共享数据库独立Schema
所有租户共享同一个数据库实例,但每个租户拥有独立的Schema(命名空间)。Schema在逻辑上隔离了表结构,不同租户的表可以同名但互不干扰。
💡 最佳实践:独立Schema模式是隔离性与资源利用率的最佳平衡点。推荐作为大多数SaaS产品的默认选择。当租户需要自定义表结构扩展时,独立Schema天然支持。
2.3.1 动态Schema管理
- 租户注册时:自动创建对应Schema,并执行初始化DDL脚本。
- 租户下线时:可快速删除整个Schema,实现数据彻底清理。
- 连接路由:应用层根据租户标识,动态切换数据库连接中的
schema或database名称。
-- 示例:为租户创建独立Schema
CREATE SCHEMA IF NOT EXISTS tenant_abc123;
-- 在租户Schema下创建表
CREATE TABLE tenant_abc123.orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_no VARCHAR(64) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
created_at DATETIME NOT NULL
);
-- 应用层动态切换Schema
-- 伪代码:connection.setSchema("tenant_" + tenantId);
2.4 模式三:独立数据库模式
每个租户拥有完全独立的数据库实例(可以是物理机或云RDS实例)。这是隔离性最强的模式,但成本最高、运维最重。
🔒 合规驱动场景:在金融、医疗、政务等强监管行业,独立数据库模式是唯一合规选项。例如:
• 金融行业要求租户数据物理隔离,审计日志独立存储。
• 医疗行业要求患者数据不得与其他机构混合存储。
• 跨国企业要求数据必须留在特定地理区域。
• 金融行业要求租户数据物理隔离,审计日志独立存储。
• 医疗行业要求患者数据不得与其他机构混合存储。
• 跨国企业要求数据必须留在特定地理区域。
2.4.1 独立数据库的挑战
- 资源碎片化:每个实例需要预留缓冲资源,整体利用率可能低于50%。
- 运维成本:需要管理数百甚至数千个数据库实例的备份、监控、升级。
- 连接风暴:大量租户同时创建连接时,应用服务器可能耗尽连接池。
// 示例:独立数据库模式的连接路由策略
public class TenantDataSourceRouter {
private Map<String, DataSource> tenantDataSources = new ConcurrentHashMap<>();
public Connection getConnection(String tenantId) {
DataSource ds = tenantDataSources.computeIfAbsent(tenantId, id -> {
// 根据租户ID从配置中心获取数据库连接信息
TenantDbConfig config = configService.getDbConfig(id);
return createDataSource(config);
});
return ds.getConnection();
}
}
2.5 三种模式架构对比图
下图从数据隔离层次和资源复用程度两个维度,直观展示三种模式的架构差异:
2.6 选择决策树
在实际项目中,可以按照以下决策路径快速确定隔离模式:
- 第一步:合规检查 — 业务是否受金融、医疗、GDPR等强监管约束?
→ 是:选择独立数据库模式。 - 第二步:租户规模评估 — 预期租户数是否超过5000?
→ 是:选择共享Schema模式(资源利用率优先)。 - 第三步:定制化需求 — 租户是否需要自定义表结构或索引?
→ 是:选择独立Schema模式。 - 第四步:运维能力 — 团队是否有能力管理数百个数据库实例?
→ 否:优先选择共享数据库方案(共享Schema或独立Schema)。
💡 混合策略建议:大型SaaS平台通常采用分层混合模式:
• 免费/试用租户 → 共享Schema(低成本快速扩展)
• 标准付费租户 → 独立Schema(隔离与性能平衡)
• 企业/合规租户 → 独立数据库(满足合规审计)
这种策略可以在一个产品内同时支持三种模式,通过租户等级动态路由。
• 免费/试用租户 → 共享Schema(低成本快速扩展)
• 标准付费租户 → 独立Schema(隔离与性能平衡)
• 企业/合规租户 → 独立数据库(满足合规审计)
这种策略可以在一个产品内同时支持三种模式,通过租户等级动态路由。
2.7 鉴权拦截链路中的隔离模式影响
不同的隔离模式直接决定了鉴权拦截链路的实现复杂度:
| 鉴权环节 | 共享Schema | 独立Schema | 独立数据库 |
|---|---|---|---|
| Token解析 | 解析出tenant_id | 解析出tenant_id | 解析出tenant_id |
| 数据源路由 | 无需路由(共用连接池) | 路由到对应Schema | 路由到对应数据库实例 |
| SQL拦截 | 强制注入tenant_id过滤 | 无需注入(Schema已隔离) | 无需注入(实例已隔离) |
| 审计日志 | 需记录tenant_id | 需记录Schema名称 | 需记录实例标识 |
| 跨租户查询 | 严格禁止(需额外校验) | 物理不可达 | 物理不可达 |