核心定位

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

详细对比

维度字段隔离(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: 显示客户列表

核心机制:通过 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

每个租户可以绑定一个套餐,套餐定义了该租户可见的菜单和可用功能。支持套餐到期自动禁用。

docs