多租戶資料庫設計:用於共用系統的ERD方法

為多租戶環境設計資料庫架構,需要仔細考慮資料隔離、可擴展性以及維護成本。實體關係圖(ERD)是這些決策的藍圖,決定了資料在各租戶之間的結構方式。選擇正確的方法會影響系統的效能、安全性以及未來的演進能力。本指南探討主要的架構模式、其對ERD的影響,以及每種策略所涉及的取捨。

Whimsical infographic illustrating four multi-tenant database design strategies: Database Per Tenant (separate cottages on islands), Schema Per Tenant (apartment building with colored floors), Shared Schema (co-working space with tenant_id name tags), and Hybrid Model (modular castle), with visual comparisons of isolation, cost, and maintenance trade-offs for SaaS architecture planning

🔍 理解資料模型中的多租戶

多租戶允許單一軟體實例服務多個客戶,通常稱為租戶。在資料庫設計的背景下,核心挑戰在於決定如何分離租戶資料,同時保持效率。ERD必須明確反映這些分離邊界。

  • 租戶: 使用系統的單一客戶或組織。
  • 共用系統: 應用程式邏輯,以及可能的底層基礎設施。
  • 資料隔離: 確保一個租戶無法存取其他租戶的資料。

設計選擇主要圍繞隔離邊界的位置展開。它是否存在於資料庫層級、結構層級或資料列層級?每種選擇都要求特定的ERD結構。

🏗️ 策略1:每個租戶對應一個資料庫

在此模型中,每個租戶都會獲得一個專用的資料庫實例。這提供了最高層級的隔離與安全性。從ERD的角度來看,所有資料庫的結構保持一致,但物理上的分離是絕對的。

📊 ERD結構

單一租戶資料庫的ERD圖形與標準的單租戶設計完全相同。不需要使用租戶ID欄位,因為資料庫邊界本身已作為過濾器。

  • 資料表結構: 資料表僅包含與特定租戶相關的資料。
  • 外鍵: 標準的參考完整性適用,且無需考慮租戶因素。
  • 索引: 為該租戶的特定資料量進行最佳化。

✅ 優勢

  • 完全隔離: 某個資料庫遭到入侵不會影響其他資料庫。
  • 客製化: 可針對特定租戶應用結構修改,而不影響其他租戶。
  • 效能: 同一連接池或磁碟I/O上不會因其他租戶而產生競爭。

❌ 缺點

  • 成本: 多個實例導致基礎設施成本高昂。
  • 維護: 模式更新需要部署到每個資料庫實例。
  • 複雜性: 隨著規模擴大,連接和編排的管理變得困難。

🏗️ 策略 2:每個租戶對應一個模式

此方法介於前兩者之間。每個租戶在相同的資料庫伺服器內獲得專用的模式。這降低了管理多個資料庫連接的開銷,同時保持了邏輯上的分離。

📊 ERD 結構

ERD 與單租戶模型保持一致,但命名空間發生變化。表格存在於特定的模式命名空間中,而非公共命名空間。

  • 表格名稱: 標準命名慣例(例如,使用者, 訂單).
  • 模式名稱: 唯一識別碼(例如,模式_租戶_a, 模式_租戶_b).
  • 連接性: 應用程式連接到活躍租戶的特定模式。

✅ 優點

  • 隔離性: 比共用模式模型具有更強的隔離性。
  • 管理: 比獨立的資料庫實例更容易管理。
  • 備份:可以獨立還原或備份單個架構。

❌ 缺點

  • 資源使用:仍然比完全共享的模型消耗更多資源。
  • 查詢複雜度:跨租戶聚合資料需要動態切換架構。
  • 架構偏移:在許多租戶之間保持架構同步是勞力密集的。

🏗️ 策略 3:共享資料庫,共享架構

這是 SaaS 應用程式中最常見的方法。所有租戶共用同一個資料庫和相同的資料表。資料分離透過唯一識別符號欄位邏輯實現。

📊 資料實體關係圖結構

資料實體關係圖必須明確包含一個tenant_id欄位於儲存租戶特定資料的每一張資料表中。此欄位作為分割金鑰。

  • 核心資料表: users, orders, products都包含一個tenant_id.
  • 共享資料表:例如rolespermissions這些資料表可能被所有租戶共用。
  • 限制條件:外鍵可能需要進行範圍設定,以確保在租戶環境中維持referential integrity(參考完整性)。

✅ 優勢

  • 成本效益:最低的基礎設施成本。
  • 維護:結構變更會立即應用於所有租戶。
  • 分析:更容易聚合資料以進行系統範圍的報告。

❌ 缺點

  • 複雜查詢: 每個查詢都需要根據 tenant_id.
  • 效能: 若某個租戶消耗過多資源,將會有很高的競爭風險。
  • 安全性: 更高的邏輯錯誤風險,可能導致資料外洩。

🏗️ 策略 4:混合模型

混合方法結合了上述策略的元素。例如,標準資料使用共享結構,但高階訂閱層級或特定高價值租戶則使用專屬結構。

📊 ERD 結構

ERD 變得更複雜,需區分共享資料表與租戶專用資料表。

  • 全域資料表: 儲存設定或共享的元資料。
  • 租戶資料表: 儲存使用者資料,並搭配 tenant_id 或儲存在獨立的結構中。
  • 連結: Join 操作必須考慮資料的範圍。

🛡️ 數據隔離與安全考量

無論選擇哪種策略,數據隔離都至關重要。ERD 必須支援防止意外數據存取的機制。

🔒 行級安全

在共享架構模型中,可以定義行級安全(RLS)策略。資料庫引擎會限制存取那些「tenant_id」與已驗證的上下文相符的資料列。

  • 實作:策略會在每次執行「SELECT, UPDATEDELETE」操作時強制執行檢查。
  • 優勢:可防止應用層錯誤導致資料外洩。
  • ERD 的影響:所有相關資料表都必須明確包含tenant_id欄位。

🔒 外鍵約束

在共享模型中,確保跨租戶的參考完整性可能相當棘手。外鍵理想上不應指向跨越多個租戶的資料表,除非該關係明確為全域性。

  • 自我引用:若資料表引用自身(例如:parent_id),則兩側的tenant_id都必須相符。
  • 全域引用:分類可能是全域的,允許任何租戶引用它們。

⚡ 性能與擴展策略

隨著租戶數量增加,性能成為關鍵考量。ERD 設計直接影響系統的擴展能力。

📈 索引策略

索引對於查詢性能至關重要。在共享架構中,tenant_id欄位應是主要複合索引的一部分,或需進行大量索引。

  • 複合索引: (tenant_id, created_at)可有效根據租戶和時間進行過濾。
  • 部分索引:索引僅能針對特定條件建立,從而減少索引大小。
  • 避免:對無法協助租戶過濾的欄位進行索引。

📦 分區

表格分區有助於管理大型資料集。資料可依 tenant_id 或依租戶內的時間範圍進行分區。

  • 範圍分區:根據日期範圍分割資料。
  • 清單分區:根據特定租戶 ID 分割資料。
  • 管理:分區可被分離或歸檔,以提升性能。

🔧 維護與架構演進

軟體會演進。需要新增表格、修改欄位或變更類型。所選架構決定了這些變更所需的投入。

🔄 架構更新

  • 共享架構:單一遷移腳本即可更新所有租戶的架構。這是最簡單的途徑。
  • 每個租戶的資料庫:遷移腳本必須針對每個資料庫執行個體執行。需要自動化。
  • 每個租戶的結構:與每個租戶的資料庫類似,但是在同一個執行個體內管理。

📝 向後相容性

修改ERD時,請確保向後相容性,以避免停機。

  • 新增欄位:首先使用可為空的欄位,然後填入資料,最後再設為不可為空。
  • 刪除欄位:在刪除欄位之前先重命名,以防止造成破壞性變更。
  • 版本控制:如果租戶可以選擇不更新,請考慮對結構本身進行版本控制。

📋 架構方法比較

功能 每個租戶的資料庫 每個租戶的結構 共用結構
隔離性
成本
維護 複雜 簡單
查詢效能 高(無過濾) 中等 可變(需要過濾)
ERD 複雜度 簡單(每資料庫) 簡單(每架構) 複雜(需要 tenant_id)
可擴展性 水平 垂直 垂直/水平

✅ 最佳實務檢查清單

在最終確定多租戶系統的ERD之前,請確保滿足以下標準。

  • 定義租戶範圍:明確識別哪些資料屬於租戶,哪些是全域資料。
  • 統一命名:對所有表格中的 tenant_id 欄位使用一致的命名慣例。
  • 強制約束:在可能的情況下,使用資料庫約束來防止跨租戶資料存取。
  • 規劃流失:設計租戶加入與退出流程(資料刪除或歸檔)。
  • 測試隔離:定期測試以確保一個租戶無法查詢另一個租戶的資料。
  • 記錄關係:在ERD文件中明確記錄外鍵關係。
  • 監控效能:為可能顯示租戶特定瓶頸的慢查詢設置警示。

🧩 處理邊界情況

現實世界的情境經常會引入標準實體關係圖無法立即涵蓋的複雜性。

🔄 租戶合併

有時,兩個租戶會合併為一個。在共享架構中,這需要將資料行從一個租戶移動到另一個。租戶識別碼到另一個。在每個租戶一個資料庫的模型中,這涉及合併兩個完整的資料庫。

  • 資料一致性: 確保合併過程中不會遺失任何資料。
  • 去重複: 處理可能因合併而產生的重複記錄。

📉 租戶流失

租戶離開。決定刪除資料或歸檔資料會影響實體關係圖。

  • 軟刪除: 加上一個 已刪除標記 標記以保留資料以符合法規要求。
  • 硬刪除: 完全移除資料行。確保級聯刪除正確配置,以避免出現孤立記錄。
  • 歸檔: 將舊的租戶資料移至冷存儲資料表,同時保持架構完整。

🔗 與應用程式邏輯整合

實體關係圖並非孤島。它必須與應用程式層無縫整合。

  • 中介軟體: 使用應用程式層級的中介軟體自動注入 租戶識別碼 到每個查詢中。
  • 物件關係映射設定: 設定物件關係映射工具以處理租戶範圍。
  • API設計: 確保API端點在返回資料前驗證租戶上下文。

🎯 設計的最後想法

為多租戶環境選擇合適的資料庫設計,是在隔離與效率之間取得平衡的過程。ERD 起到合約的作用,定義了這些界限。並不存在單一的完美解決方案;選擇取決於安全、成本和規模等方面的具體需求。透過理解每種策略的影響,架構師可以建立穩健、可擴展且安全的系統。

專注於清晰的資料模型設計實務,可確保系統在租戶數量增加時仍具可維護性。定期根據現實使用模式檢視ERD,有助於在瓶頸或安全漏洞演變為關鍵問題之前及時發現。

最終目標是設計出在不損害資料完整性的前提下支援業務的系統。在ERD階段進行仔細規劃,可避免後續產生高昂的重構成本。