為什麼不用外來鍵

JethroYu發表於2020-08-27

是否使用外來鍵約束

【強制】不得使用外來鍵與級聯,一切外來鍵概念必須在應用層解決.-《阿里Java規範》

首先外來鍵(Foreign Key)是什麼東西

使用方案

假設有一個score表 id是自增id,score是分數,student_id是學號。

另一個student表,id是自增id,name是名字,student_id是學號。

那麼設計這個的時候就希望有一個關聯關係,讓score的student_id指向student表的student_id,存在一個學生對應多個成績的關係。所以我可以使用以下SQL語句

ALTER TABLE score
ADD CONSTRAINT FOREIGN KEY (student_id)
REFERENCES student(student_id);

建立一個外來鍵索引完成這個規則

完成後的表關係如下

外來鍵原理

被指向的欄位,具有唯一性

可以保證成績欄位的一致性,即每一次插入一個score資料,首先要檢測是否student表存在這個id,保證一致性

如果在外來鍵型別上使用CASCADE,則會保證在做更新和刪除sutudent表的student_id時,觸發一次級聯操作,會同步更新score表的student_id或者刪除student_id.

外來鍵型別RESTRICT 也同樣會做一次檢測,但不會做級聯操作,而是直接拒絕操作。

場景思考

知道外來鍵是什麼後,我們來思考一個場景:

現在有一個電商系統,使用者有一個賬戶id,商品有一個商品id,這兩個欄位和訂單繫結,此時訂單id和賬戶表ID構成一個外來鍵關係,同時和商品表id也構成一個外來鍵關係,那麼我每次生成一筆訂單,就需要向另外兩張表查詢檢測一次資料,那麼就存在幾個問題:

  • 增刪改觸發這個查詢操作的效能消耗,伺服器系統是否允許
  • 在其他表查詢會上需要對其他表做一個內部鎖,是否存在高併發死鎖情況
  • 資料一致性全部交給資料庫伺服器,資料庫伺服器是否能夠承受

這些問題在網際網路公司會顯得格外嚴重,因為訪問流量大的時候以上問題基本上是完全無法得到MySQL系統本身解決的

同時在做分庫分表設計的時候,外來鍵約束就會顯得格外離譜。

同時MySQL系統的外來鍵設計是背離部分SQL標準的

引用自部落格園Eden: (https://www.cnblogs.com/discuss/articles/1862244.html)

對SQL標準的背離:如果ON UPDATE CASCADE或ON UPDATE SET NULL遞迴更新相同的表,之前在級聯過程中該表一被更新過,它就象RESTRICT一樣動作。這意味著你不能使用自引用ON UPDATE CASCADE或者ON UPDATE SET NULL操作。這將阻止級聯更新導致的無限迴圈。另一方面,一個自引用的ON DELETE SET NULL是有可能的,就像一個自引用ON DELETE CASCADE一樣。級聯操作不可以被巢狀超過15層深。

對SQL標準的背離: 類似一般的MySQL,在一個插入,刪除或更新許多行的SQL語句內,InnoDB逐行檢查UNIQUE和FOREIGN KEY約束。按照SQL的標準,預設的行為應被延遲檢查,即約束僅在整個SQL語句被處理之後才被檢查。直到InnoDB實現延遲的約束檢查之前,一些事情是不可能的,比如刪除一個通過外來鍵參考到自身的記錄。

處理

因為以上問題,我們通常在建模時隱性設計外來鍵約束,實際實現採用業務邏輯模擬外來鍵的方式處理,這樣可以解決把一致性全部放在DBA上的效能問題,同時我們可以採用允許髒資料存在,然後定時資料清理的方案去保證資料處理的分時效能,避免高峰處理。

這樣的好處:

  • 解決效能問題
  • 增加了可擴充套件性,框架遷移不用在資料庫系統內部實現邏輯約束
  • 分庫分表的時候方便
  • 不會在DB層面造成死鎖

反推:是否可以使用外來鍵

我覺得在部分業務場景下是可以考慮使用的,回到最開始的例子,教務系統的成績模組重要的點不再是效能問題,而是高可靠,因為對學校來說,系統存在以下特點:

  • 資料量較少,一個學校學生最多不超過10萬人,通常在5000-50000這個區間內,對DB來說這是一個很小的資料量
  • 資料不容許出錯,因為成績和學生的人身利益直接掛鉤
  • 能夠進行資料修改操作的使用者極少,只有教務處錄入成績的老師。
  • 如果放在業務部分,如果出現student表student_id在第一次被刪除後,未清理score表資料,這個student_id短時間內被再次使用,而沒有做資料清理,就容易出現成績複用錯誤,諸如此類

所以 不得使用外來鍵與級聯,一切外來鍵概念必須在應用層解決。 大部分情況下正確,但同樣我認為需要分業務場景解決,並不能一竿子打死。

相關文章