SaaS 多租户
核心定位
SaaS 多租户让你用一套代码同时服务多个企业客户,每个客户(租户)只能看到自己的数据,彼此完全隔离。无论是做垂直 SaaS 产品还是企业内部多子公司管理,这都是不可或缺的能力。
两种方案:字段隔离(轻量级,适合中小规模)和数据库隔离(物理隔离,适合大规模 SaaS)。
适合什么样的用户
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| SaaS 创业初期 | 字段隔离 | 运维简单,一台数据库搞定 |
| 大型 SaaS 平台 | 数据库隔离 | 物理隔离,安全性高,可独立备份 |
| 企业内部多公司 | 字段隔离 | 公司数量有限,逻辑隔离足够 |
| 金融/医疗等强合规行业 | 数据库隔离 | 数据必须物理隔离 |
两种方案对比
graph TB
subgraph COLUMN["方案一:字段隔离 COLUMN 模式"]
direction TB
C1["所有租户共享
同一数据库同一套表"] C2["每张表添加
tenant_id 字段"] C3["SQL 拦截器
自动拼接 WHERE tenant_id = ?"] C4["优点:运维简单、资源利用率高
缺点:逻辑隔离,安全性一般"] C1 --> C2 --> C3 --> C4 end subgraph DATASOURCE["方案二:数据库隔离 DATASOURCE 模式"] direction TB D1["主库:共享表
用户/菜单/配置/租户信息"] D2["租户库 A:业务表"] D3["租户库 B:业务表"] D4["租户库 C:业务表"] D5["优点:物理隔离、安全性高、可独立备份
缺点:运维复杂、资源利用率低"] D1 --> D2 D1 --> D3 D1 --> D4 D2 & D3 & D4 --> D5 end
同一数据库同一套表"] C2["每张表添加
tenant_id 字段"] C3["SQL 拦截器
自动拼接 WHERE tenant_id = ?"] C4["优点:运维简单、资源利用率高
缺点:逻辑隔离,安全性一般"] C1 --> C2 --> C3 --> C4 end subgraph DATASOURCE["方案二:数据库隔离 DATASOURCE 模式"] direction TB D1["主库:共享表
用户/菜单/配置/租户信息"] D2["租户库 A:业务表"] D3["租户库 B:业务表"] D4["租户库 C:业务表"] D5["优点:物理隔离、安全性高、可独立备份
缺点:运维复杂、资源利用率低"] D1 --> D2 D1 --> D3 D1 --> D4 D2 & D3 & D4 --> D5 end
详细对比
| 维度 | 字段隔离(COLUMN) | 数据库隔离(DATASOURCE) |
|---|---|---|
| 隔离级别 | 逻辑隔离 | 物理隔离 |
| 安全性 | 中等(依赖 SQL 拦截) | 高(数据库级隔离) |
| 资源利用率 | 高(共享连接池) | 低(每租户独立连接) |
| 运维复杂度 | 低(单库运维) | 高(多库运维) |
| 扩展性 | 受限于单库性能 | 可水平扩展 |
| 数据备份 | 需要按 tenant_id 筛选 | 每个租户独立备份 |
| 适用规模 | 百级租户 | 千级/万级租户 |
| 技术实现 | MyBatis Plus 拦截器 | dynamic-datasource |
字段隔离实现原理
sequenceDiagram
participant U as 用户(租户A)
participant APP as 应用层
participant INTERCEPTOR as SQL 拦截器
participant DB as 共享数据库
U->>APP: 查询客户列表
APP->>INTERCEPTOR: SELECT * FROM crm_customer
INTERCEPTOR->>INTERCEPTOR: 自动拼接 tenant_id = 'A'
INTERCEPTOR->>DB: SELECT * FROM crm_customer
WHERE tenant_id = 'A' DB-->>APP: 只返回租户A的数据 APP-->>U: 显示客户列表
WHERE tenant_id = 'A' DB-->>APP: 只返回租户A的数据 APP-->>U: 显示客户列表
核心机制:通过 MyBatis Plus 的多租户插件,在执行任何 SQL 前自动添加 tenant_id 条件,对业务代码完全透明。
数据库隔离实现原理
基于 dynamic-datasource 动态数据源,请求进入时根据当前租户 ID 切换到对应的数据源:
spring:
datasource:
dynamic:
primary: master # 主库(共享表)
datasource:
master: # 主库配置
url: jdbc:mysql://localhost:3306/ruoyi_master
tenant_1: # 租户1的独立数据库
url: jdbc:mysql://localhost:3306/ruoyi_tenant_1
tenant_2: # 租户2的独立数据库
url: jdbc:mysql://localhost:3306/ruoyi_tenant_2
租户套餐管理
graph LR
PACKAGE1["基础版套餐
系统管理 + 基础功能"] PACKAGE2["专业版套餐
基础版 + CRM + ERP"] PACKAGE3["旗舰版套餐
全部功能"] 租户A["租户 A"] --> PACKAGE1 租户B["租户 B"] --> PACKAGE2 租户C["租户 C"] --> PACKAGE3
系统管理 + 基础功能"] PACKAGE2["专业版套餐
基础版 + CRM + ERP"] PACKAGE3["旗舰版套餐
全部功能"] 租户A["租户 A"] --> PACKAGE1 租户B["租户 B"] --> PACKAGE2 租户C["租户 C"] --> PACKAGE3
每个租户可以绑定一个套餐,套餐定义了该租户可见的菜单和可用功能。支持套餐到期自动禁用。