MySQL 高級特性(五):爲什麼不推薦你使用外鍵?

在早期的數據庫表結構設計中,往往會把一張表引用另外一張表的字段(通常是 id)作爲外鍵,藉助 MySQL 自動維護外鍵,確實能夠省掉很多開發工作,但是外鍵實際的代價不低,很多數據表設計規範已經明確禁止使用外鍵。本篇將介紹外鍵存在的缺陷。

目前,在 MySQL 內置存儲引擎中,只剩下 InnoDB 還在支持外鍵,因此如果要使用外鍵可選擇的存儲引擎不多。

外鍵並不是沒有代價的。事實上,外鍵通常會需要服務器地在更改數據的時候檢查另一張表。儘管 InnoDB 使用了索引提高這個操作速度,但並沒有讓數據檢查的影響消失。甚至還可能產生一個很大的沒有篩選性的索引。例如,假設我們在大的數據表中有個 status 字段,然後約束 status 只能使用有效的值,然而這個值只有三個可選項。這會導致額外的索引大大增加數據表的存儲空間大小,即便是這個列自身存儲空間很少。尤其是當主鍵存儲空間比較大時,而這個索引除了用於外鍵檢查,通常沒什麼用。

當然,外鍵在某些情況也能夠改善性能。如果我們需要保證兩個關聯表的數據必須保持一致的時候,使用 MySQL 服務器進行檢測會比程序來檢測效率更高。外鍵在那種級聯刪除或更新的場合也很有用。但是這類操作是逐行進行的,因此會比批量刪除或批量操作更慢。

外鍵導致查詢需要依賴其他數據表,這意味着 InnoDB 需要在父級表(或相關表)中檢驗相應的值。這也會鎖定父級表的數據行,以保證在事務完成前該行不會被刪除。這會導致意外的鎖等待,甚至是死鎖,這類問題很難被定位。

既然外鍵有如此多的缺陷,因此可以使用別的方式替代外鍵。一種方式是使用觸發器替代外鍵。外鍵的目的是在類似級聯更新的時候觸發任務自動關聯。對於那些僅僅是約束值的外鍵,例如之前談到的 status 例子,可以通過一個明確的可用值列表的觸發器重寫(也可以使用 ENUM 枚舉類型)。

另一種方式是使用程序來完成類似外鍵的約束。外鍵可能會顯著增加負荷,這個雖然沒法準確度量,但是實際確實發生過很多由於外鍵約束檢查引起的性能問題,而且移除外鍵後性能能夠得到顯著提升。

結語:從衆多互聯網企業的數據庫設計規範來看,外鍵是一再被重申要被禁用的。不單單是性能問題,而且互聯網的業務多變,如果是表結構發生變動,很可能會導致外鍵關聯的表出現意想不到的問題。因此,在非必要的情況下不要用外鍵,除非你只是爲了驗證外鍵的功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章