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
每個租戶可以繫結一個套餐,套餐定義了該租戶可見的選單和可用功能。支援套餐到期自動禁用。