實體關係圖(ERD)是資料庫架構的藍圖。它定義了資料在應用程式中如何被結構化、儲存與連結。對於資深後端開發者而言,設計穩健資料結構的能力是一項基本技能。然而,經驗有時會導致自滿。即使資深工程師也會陷入會損害資料完整性、系統效能與長期可維護性的陷阱。
本指南探討在ERD設計階段常見的陷阱。我們將分析具體的技術錯誤、其後果,以及避免這些錯誤的策略。重點始終放在基礎原則上,而非特定工具或平台。

1. 錯誤理解基數約束 🔄
基數定義了實體之間的數值關係。錯誤地映射這些關係,可能是資料異常最常見的來源。資深開發者經常急於完成這一步驟,假設關係顯而易見,而未進行明確的驗證。
一對一混淆
假設存在一對一關係,而實際上是一對多關係,可能導致資料遺失。例如,若「使用者」實體與「個人檔案」實體以一對一方式連結,但業務邏輯允許隨時間產生多個個人檔案,則資料結構會強制刪除舊資料。
- 影響:歷史資料將無法存取。
- 修正:檢視資料的生命周期。實體是持續存在,還是取代另一個實體?
多對多疏忽
在沒有中間關聯表的情況下,直接使用多個外鍵連結兩個資料表,會造成冗餘。多對多關係需要一個關聯實體。
- 影響:資料重複與更新異常。
- 修正:引入關聯表以解決此關係。
2. 過早進行效能優化 🚀
將資料規範化至極限(第三範式)以減少儲存空間,這誘人之舉。相反地,有些開發者過早進行反規範化以加快讀取速度。兩種極端都可能帶來問題。
過度規範化
為微不足道的細節創建過多資料表,會增加取得資料所需的連接次數。這會導致查詢執行速度變慢,尤其是在高負載下。
- 情境:在使用者記錄中僅需一次使用地址時,仍將地址儲存在獨立的資料表中。
- 後果:複雜的查詢,難以維護與優化。
規範化不足
在多個表格之間複製資料以避免連接會造成極高的不一致風險。如果使用者更改了姓名,您必須在每個儲存該資料的表格中更新它。
- 情境:將產品名稱直接嵌入訂單記錄中。
- 後果:如果後續產品細節變更,將會產生資料完整性問題。
3. 模糊的命名規範 📝
清晰的命名是文件編寫與溝通的基石。當資料表或欄位名稱模糊不清時,ERD 對未來的開發人員而言將變成一團謎題。資深開發人員應強制執行嚴格的標準。
- 資料表名稱: 使用複數名詞(例如,
users而非user). - 外鍵: 一致地命名(例如,
user_id而非uid或fk_user). - 布林欄位: 前綴加上
is_或has_(例如,is_active).
模糊不清會導致錯誤,例如開發人員查詢了錯誤的欄位,或錯誤地假設某個關係存在,而實際上並不存在。
4. 忽略軟刪除和審計欄位 ⏳
硬刪除會永久移除資料。在許多系統中,這並非理想做法。高階設計應考慮軟刪除(將記錄標記為非活躍狀態,而非直接移除)。
缺少時間戳
每個資料表都應記錄資料列的建立時間和最後修改時間。若沒有created_at與updated_at欄位,調試資料歷史幾乎變得不可能。
忽略軟刪除標誌
若沒有類似deleted_at的標誌,刪除記錄會影響所有依賴該記錄的歷史報表。這會破壞審計追蹤並違反合規要求。
5. 電腦循環依賴與自我引用 🔁
複雜的層次結構經常導致循環外鍵。例如,若資料表A引用資料表B,而資料表B又引用資料表A,就會形成一個循環。
- 問題:這可能阻止資料庫的初始化,或在遞迴查詢期間導致無限循環。
- 自我引用: 資料表引用自身(例如,
employees引用manager_id於同一資料表內)需要仔細的約束管理。
設計這些結構時,應確保至少有一個實體能獨立存在,而不依賴於另一個。
6. 資料類型與精確度錯誤 📏
選擇錯誤的資料類型是一項微妙但關鍵的錯誤。這會影響儲存空間大小、效能與計算精確度。
浮點數 vs. 十進位數
使用浮點數處理貨幣是一項經典錯誤。浮點數運算會引入四捨五入錯誤,在金融環境中是不可接受的。
- 建議:貨幣應使用固定精度的十進位類型。
字串長度限制
將欄位設為VARCHAR(255)預設可能看起來安全,但如果實際資料較短,就會浪費空間。相反地,VARCHAR(50)可能對於現代的使用者名稱或地址來說太短了。
- 建議:在設定限制前,先分析實際的資料需求。
7. 缺乏文件和註解 📄
ERD 是一份持續更新的文件。若沒有註解說明商業規則,圖表隨時間推移會失去價值。資深開發人員應記錄那些不顯而易見的限制條件。
- 商業規則:說明為什麼某個關係是可選的。
- 限制條件:記錄唯一性限制與檢查限制。
- 演進:記錄為何做出特定設計決策,以供未來參考。
8. 將領域邏輯與資料庫結構設計混合 🧠
資料庫結構應僅用於儲存資料,而非邏輯。將商業規則直接嵌入資料庫層(例如透過觸發程序或儲存程序)會使系統難以遷移或擴展。
- 不良做法:在資料庫中強制執行驗證邏輯。
- 良好做法:保持結構簡單,並將邏輯移至應用層。
這種分離確保即使應用程式程式碼變更,資料庫仍能保持穩定。
9. 忽略可擴展性與分割 📈
適用於小資料集的設計在擴展時經常失敗。資深開發人員必須預見成長需求。
- 索引:為用於搜尋與連接運算的欄位規劃索引。
- 分割:考慮當資料表成長到數十億列時,應如何進行分割。
- 分片:了解將使用哪些金鑰來將資料分片至多個伺服器上。
對比:常見錯誤與最佳實踐
| 領域 | 常見錯誤 ❌ | 最佳實踐 ✅ |
|---|---|---|
| 關係 | 在沒有證據的情況下假設 1:1 | 根據業務需求驗證基數 |
| 效能 | 過度規範化以節省儲存空間 | 在規範化與查詢需求之間取得平衡 |
| 命名 | 簡短且含糊的別名 | 描述性且一致的命名標準 |
| 歷史 | 僅執行硬刪除 | 實施軟刪除與審計日誌 |
| 金錢 | 使用 Float/Double | 使用 Decimal/定點類型 |
| 邏輯 | 使用觸發器進行驗證 | 應用層級的驗證 |
| 成長 | 沒有索引策略 | 早期規劃索引與分割 |
10. 與前端團隊的溝通落差 🤝
資料庫結構並非在真空環境中建立。它必須支援前端應用程式所使用的 API 合約。ERD 與 API 回應結構之間的不一致會造成摩擦。
- 命名衝突:資料庫欄位通常使用 snake_case,而 API 使用 camelCase。確保有明確的對應策略。
- 資料暴露: 不要在公開 API 中暴露內部 ID(例如
user_id)除非必要,否則不要在公開 API 中暴露。如果安全是考量因素,請使用不透明的識別碼。 - 版本控制: 計畫 schema 迁移。ERD 的變更不應破壞現有的客戶端。
11. 安全考量 🔒
安全在 ERD 設計中經常被忽略。敏感資料需要特別處理。
個人識別資訊(PII)與加密
個人識別資訊(PII)必須在資料結構中明確標示。包含電子郵件、電話號碼或地址的欄位應標記為需要加密或雜湊處理。
存取控制
雖然資料庫負責處理資料列層級的安全性,但資料結構應支援此功能。若需多租戶架構,應設計允許租戶隔離或基於角色的存取控制的資料表。
12. 人性因素:審查與協作 👥
即使是最優秀的設計師也會遺漏細節。同儕審查至關重要。一雙新鮮的眼睛可能發現原始設計者忽略的循環依賴或命名衝突。
- 設計審查: 計畫會議,逐行走查 ERD。
- 利害關係人反饋: 確保領域專家確認資料模型符合現實世界的流程。
- 文件化: 確保圖表與程式碼庫保持同步。
重點總結 📌
- 驗證基數: 永遠不要假設關係。應根據業務規則進行驗證。
- 平衡正規化: 同時優化儲存空間與查詢效能。
- 統一命名: 在整個資料結構中使用清晰且一致的命名慣例。
- 規劃歷史紀錄: 實作軟性刪除與稽核時間戳記。
- 謹慎選擇資料類型: 金錢使用小數類型,字串則使用適當長度。
- 分離邏輯:將資料庫用於資料,而非業務規則。
- 記錄所有內容:解釋設計決策背後的「原因」。
- 考慮擴展性:從第一天起就考慮索引和分區設計。
- 協作:讓前端開發人員和相關利益者參與設計過程。
設計實體關係圖是一項關鍵任務,為整個應用程式奠定基礎。透過避免這些常見錯誤,資深後端開發人員可確保其系統具備穩健性、可維護性,並具備成長的準備。目標不僅是儲存資料,更要以一種能長期支援業務的方式來結構化資料。











