為多租戶環境設計穩健的資料庫結構,與單租戶架構相比,需要根本性的思維轉變。當多個客戶(租戶)共用相同的基礎設施時,實體關係圖(ERD)便成為資料隔離、安全與效能的藍圖。🏗️ 設計不良的ERD可能導致資料外洩、效能下降,以及複雜的遷移路徑。本指南探討在不依賴特定軟體工具的情況下,建模多租戶系統的結構細節,重點放在架構原則上。

理解共用資料的核心挑戰 🏢
在傳統的單租戶設定中,每位客戶都擁有自己獨立的資料庫。應用程式與資料之間的關係是一對一。然而,在多租戶系統中,關係則是一對多。應用程式從共用資源池中為多個租戶提供服務。ERD必須明確地將租戶的上下文編碼到每一筆查詢與交易中。
主要目標是確保租戶A永遠不會看到屬於租戶B的資料,即使他們查詢的是完全相同的資料表。這通常被稱為邏輯隔離。ERD必須透過資料結構設計原生支援此種隔離,而非僅依賴應用程式邏輯。🔒
隔離模型及其對資料結構設計的影響 🏗️
有三種主要的租戶資料隔離模型。每種模型都決定了實體關係圖截然不同的設計方式。在設計初期選擇錯誤的模型,後續將被迫進行成本高昂的重寫。
1. 每租戶一個資料庫(物理隔離)
在此模型中,每位租戶都擁有自己的物理資料庫實例。ERD與單租戶設計完全相同。每個資料表都獨立存在於各自的資料庫容器中。
- 優點:安全性與隔離程度達到最大。租戶之間的資料外洩在物理上不可能發生。
- 缺點:營運成本高。管理數百甚至數千個資料庫相當複雜。
- 資料結構影響:ERD無需考慮租戶識別欄位,因為資料庫本身即為識別依據。
2. 每租戶一個資料結構(邏輯隔離)
多位租戶共用單一資料庫,但每位租戶在該資料庫內擁有自己獨立的資料結構(命名空間)。ERD與單租戶版本大致相同,但資料結構名稱會根據租戶而變更。
- 優點:比共用資料表具有更好的隔離性。管理上比單獨的資料庫更簡單。
- 缺點:查詢複雜度增加,因為應用程式必須動態切換資料結構。
- 資料結構影響:ERD無需在每個資料表中加入租戶ID欄位。相反地,由資料庫連接的上下文來處理分離。
3. 共用資料結構,共用資料表(邏輯隔離)
這是SaaS應用程式中最常見的模型。所有租戶共用完全相同的資料表。ERD必須進行修改,以在每一筆相關資料列中加入每位租戶的唯一識別碼。
- 優點:成本最低,營運開銷最小。執行全域分析更為容易。
- 缺點:若邏輯失敗,資料外洩風險最高。隨著資料表規模擴大,效能可能下降。
- 資料結構影響: 每個表格都必須包含一個
tenant_id欄位。外鍵必須參考此欄位以維持完整性。
設計共用架構的ERD 🔑
採用共用架構模型時,ERD需要進行特定修改,以確保資料完整性和安全性。本節將詳細說明您的圖表中必須包含的關鍵元件。
租戶識別欄位
所有儲存使用者特定資料的表格都必須包含一個欄位,用以識別該資料的所有者。此欄位通常命名為tenant_id 或 organization_id.
- 資料類型: 應為整數或UUID。整數在連接操作中通常更快。
- 不可為空約束: 此欄位永遠不應允許為空。空值表示資料無主,這違反了多租戶合約。
- 預設值: 在某些應用程式中,預設值可能在應用程式層級設定,但資料庫架構應強制要求此值存在。
外鍵關係
當表格彼此相關時,關係必須尊重租戶邊界。一個常見錯誤是建立全域表格(如產品目錄)與租戶特定表格(如訂單)之間的關係。
- 全域表格: 如
Products或Categories可能被共用。它們不需要tenant_id. - 租戶表格: 如
Orders或使用者必須具有tenant_id. - 合併邏輯: 當將全域資料表與租戶資料表合併時,合併條件必須包含
tenant_id以防止跨租戶資料外洩。
比較隔離策略 📊
理解權衡關係對於選擇正確的ERD結構至關重要。下表概述了主要隔離策略之間的主要差異。
| 策略 | 隔離層級 | 成本 | 管理複雜度 | 結構需求 |
|---|---|---|---|---|
| 每個租戶一個資料庫 | 物理 | 高 | 高 | 標準(無租戶ID) |
| 每個租戶一個結構 | 邏輯 | 中等 | 中等 | 標準(結構名稱) |
| 共用結構 | 資料列層級 | 低 | 低 | 需要租戶 ID 欄位 |
ERD 設計中的效能考量 🚀
隨著資料累積,共用結構的效能可能會下降。ERD 必須支援能針對租戶特定查詢進行最佳化的索引策略。
索引策略
若沒有適當的索引,用來取得單一租戶資料的查詢可能會掃描整個資料表,其中包含來自其他租戶的數百萬筆資料。
- 複合索引:建立以
tenant_id為起點的索引。例如,在 (tenant_id,created_at) 上建立的索引,可讓資料庫快速定位特定租戶的記錄並進行排序。 - 覆蓋索引: 若您經常查詢特定欄位,請將這些欄位包含在索引中,以避免資料表查找。
- 分割:大型資料表可依
tenant_id進行分割。這會在磁碟上物理性地分離資料,提升查詢速度並改善備份管理。
查詢最佳化
應用層必須確保每個查詢都包含 tenant_id 在 WHERE子句中。ERD 設計不應依賴應用程式來過濾資料;資料庫應為唯一真實來源。
- 資料列層級安全性: 某些資料庫系統支援資料列層級安全性(RLS)。ERD 可利用此功能,根據已驗證使用者的上下文自動過濾資料列。
- 查詢計畫:定期檢視查詢執行計畫。確保資料庫正在使用
tenant_id索引且不執行完整表格掃描。
安全與合規性影響 🛡️
資料隱私法規(如GDPR和CCPA)對資料的儲存與存取方式設有嚴格要求。ERD在合規性方面扮演著關鍵角色。
資料隔離
合規性通常要求資料能輕易分離。若租戶要求刪除其資料,系統必須能定位並移除與其相關的所有記錄。tenant_id.
- 軟刪除: 不直接硬性刪除資料列,而是標記為已刪除。這通常對審計更安全。
deleted_at欄位也應根據tenant_id. - 加密: 租戶範圍內的敏感欄位應予以加密。金鑰管理策略必須與租戶隔離模型一致。
審計與記錄
審計追蹤對於安全至關重要。對租戶資料所執行的每一項操作都應被記錄。
- 審計表格: 建立專用的記錄表格,其中包含受影響實體的
tenant_id所屬實體。 - 存取控制: 確保審計記錄本身受到保護。管理員不應能查看其未管理租戶的審計記錄。
結構演進與遷移 🔄
應用程式不斷演進,功能被新增,資料結構也會改變。在多租戶環境中,結構遷移更加複雜,因為必須在不造成停機或資料遺失的情況下,將變更套用至所有租戶。
向後相容性
修改ERD時,請確保維持向後相容性。
- 新增變更: 若欄位允許空值,向資料表新增欄位通常是安全的。
- 欄位移除: 這很危險。在確保沒有租戶使用該欄位並設立棄用期間後,才能刪除欄位。
- 重新命名欄位: 這可能會導致查詢失效。最好新增一個欄位,遷移資料,再切換參考,而不是直接重新命名。
零停機遷移
對於大型租戶而言,在遷移期間鎖定表格並非可行方案。ERD 設計應支援線上結構變更。
- 幽靈表格: 建立一個具有更新結構的新表格,複製資料,然後交換表格。
- 版本控制: 某些系統支援同時存在多個結構版本,以實現逐步推出。
常見陷阱,應避免 ⚠️
設計多租戶 ERD 涉及許多變動部分。以下是一些會損害系統的常見錯誤。
- 忽略租戶 ID: 忘記在開發期間建立新表格時加入
tenant_id到新建立的表格中。這會立即帶來資料外洩的風險。 - 硬編碼 ID: 永遠不要在應用程式程式碼中硬編碼租戶 ID。必須在執行時動態傳遞。
- 全域計數器: 如果全域自動遞增計數器出現在 URL 或 API 回應中,應避免使用,因為這可能暴露租戶或使用者的數量。
- 共用檔案: ERD 關注資料庫,但檔案儲存常被忽略。請確保檔案路徑包含租戶識別碼,以避免存取問題。
複雜情境的進階模式 🔍
並非所有多租戶系統都相同。有些系統需要對資料結構有更細緻的控制。
多組織支援
一個租戶可能屬於多個組織,反之亦然。ERD 必須支援多對多關係。
- 關聯表格: 使用關聯表格來連結使用者、租戶與組織。
- 權限模型: ERD 應支援租戶層級的角色權限控制(RBAC)。
全域設定與租戶特定設定
某些設定資料是全域的(應用程式範圍內),而其他資料則是特定於租戶的。
- 設定表格:設計ERD以區分全域設定與租戶特定的覆蓋設定。
- 繼承:租戶設定可能繼承自全域預設值。資料結構應明確反映此層級關係。
最佳實務總結 ✅
建立安全且可擴展的多租戶系統,很大程度上取決於實體關係圖所奠定的基礎。遵循以下原則,可確保長期穩定性。
- 一致性:確保每個儲存使用者資料的表格都包含租戶識別碼。
- 隔離:選擇符合您安全與成本需求的隔離模型。
- 效能:設計以租戶識別碼為優先的索引。
- 安全性:在適當情況下實施資料列層級的安全性與加密。
- 可維護性:規劃不會中斷服務的資料結構變更。
您的資料庫結構設計是一項戰略性決策,影響應用程式的整個生命週期。良好的實體關係圖可防止資料外洩,確保合規性,並支援成長。在設計階段仔細考慮多租戶的細節,將建立一個具韌性且安全的基礎。 🏛️
隨著應用程式成長,持續檢視ERD是必要的。新功能經常引入新的資料關係,必須根據租戶隔離規則進行評估。保持警覺,記錄您的設計決策,並始終將資料完整性放在首位。這種做法可確保您的架構在擴展時仍保持穩健。











