標籤:

使用外鍵約束還是還是自己來寫代碼做約束?

對主從表的引用完整性,有兩種觀點:

1,用外鍵約束。

2,自建程序,來控制這種引用完整性。

不理解為什麼會有人說2比1快?舉例最好。

瀏覽了很多國外網站,沒找到誰自己建程序來保證這種完整性的。

再說,對有主從關係的表,您的自建程序在增、刪、改它時到底作不作完整性檢查,如果自己寫代碼來檢查,肯定不如外鍵約束快;如果不做檢查,這當然快,但您怎麼能保證引用完整性呢?原聞其詳。而且要是您寫的代碼快於資料庫的外鍵約束,您可以貼出來共勉。

還有,假設是程序員自己做外鍵約束,如何保證並發性?比如A同學發布了一個狀態,B同學給這個狀態留言。但是B同學非常喜歡A,想了好久才寫留言,但是這個時候A同學已經將狀態給刪了,這個時候,如果沒有外鍵約束,B同學會顯示留言成功,而事實上留言雖然插入資料庫,但是卻不可能再顯示了。


根源上來說,關係資料庫支持關係主要還是因為性能需求。在沒有可以簡單承載足夠多數據和計算量的客戶端或者伺服器的時候,業務邏輯直接綁定在數據上是簡單有效可靠的法子。

所以簡單說,如果你的目的是為了提高性能,那當然僅就關係約束而言外鍵約束無論如何是不慢於外部邏輯的。

但現在這世界不是這麼單純美好了。

比如題主的舉例。插入新的數據2,外鍵了已經不存在的數據1。性能上說,外部邏輯約束自然要多一道檢查,還可能要處理典型的分布一致性問題,代碼顯然不簡單,性能自然也遠不如外鍵。

但,你的業務邏輯是這麼簡單的嗎?比如知乎,我給一個答案寫個評論,點發送的時候這個答案刪除了。大家知道知乎答案不會真的刪除(特殊情況除外),那麼如何僅僅用外鍵邏輯來保證我的評論發布正確地失敗掉?一旦你開始寫額外的邏輯來處理這個問題,那外鍵還有必要嗎?外部邏輯一定比外鍵慢嗎?

另外就是從DevOps的角度看,業務邏輯與數據解耦不僅是推薦的,更是應該的。因為一旦耦合起來就意味著你終究會遇到一種情況,就是你的業務邏輯和數據必須同時修改。對常見的升級、灰度等操作都是非常大的麻煩。

簡單總結,外鍵相比外部邏輯基本只有性能和代碼複雜度的優勢,而且還是在特定的簡單條件下。選擇合適的方案即可。

當然如果你要說外鍵快不快,我會告訴你絕大多數時候外鍵和香港記者跑的一樣快。


有必要提醒一下題主,古時候mysql超級垃圾,但是用的人多,所以誕生了很多奇怪的說法,譬如說不要外健,譬如不要嵌套join,譬如不要對子查詢做露骨的事情等,這就跟現在用windows還要給硬碟分區一樣。題主查資料的時候注意分辨。

免費資料庫站SQL Server ecpress和postgresql。有條件就不要用mysql,對正確掌握概念極其有幫助。


如果看過阿里開源的 java 規範,你會發現禁止使用外鍵。一般如果是 web 應用並且數據將來增長很快,最好還是用程序來保證。其實很多互聯網公司都有類似定義:

  1. 【強制】不得使用外鍵與級聯,一切外鍵概念必須在應用層解決。

    說明:以學生和成績的關係為例,學生表中的 student_id 是主鍵,那麼成績表中的 student_id

    則為外鍵。如果更新學生表中的 student_id,同時觸發成績表中的 student_id 更新,即為

    級聯更新。外鍵與級聯更新適用於單機低並發,不適合分散式、高並發集群;級聯更新是強阻塞,存在資料庫更新風暴的風險;外鍵影響資料庫的插入速度。

(13)禁止使用外鍵,如果有外鍵完整性約束,需要應用程序控制

解讀:外鍵會導致表與表之間耦合,update與delete操作都會涉及相關聯的表,十分影響sql 的性能,甚至會造成死鎖。高並發情況下容易造成資料庫性能,大數據高並發業務場景資料庫使用以性能優先

參考:

《阿里巴巴Java開源手冊》

MySQL互聯網業務資料庫設計規範


都是代碼來處理外鍵的,用外鍵處理這樣邏輯和處理能力必然被資料庫約束,涉及到分散式和跨庫是沒辦法處理。

早期的時候軟體能力比較弱的時候,資料庫是最強大的工具,必須用依賴資料庫來處理高性能和複雜邏輯。

現在大部分應用是分散式和應用化,資料庫應該被退化成存儲工具,通過api來處理業務。

存儲過程早就被退化了,外鍵也被退化了,主鍵也快要被退化了。


看你實際需求啦,「有人說XXXX」 總是因為別人有你不了解的需求,而你也沒有遇到過這樣的需求。

一種最常見的問題莫過於,資料庫性能擴展常用水平拆分來縮減單表單庫的大小,這已經形成了很多成熟的方法論Shard (database architecture),最常見的莫過於基於shard key來做分庫分表,很多第三方魔改mysql都已經實現了自動化的基於shard key 來分庫分表,自動擴容,還能在此基礎上實現主備切換等等很多自動化運維操作。 shared disk 來搞的垂直拆分實際中因為需要硬體支持且對性能提升也不大比較少有實現。

然而想要基於shard key來做操作,很多形式的跨表sql 或者跨表約束肯定是會受限制的。因為如果本來你眼中屬於同一個庫的多表操作和約束,底層實際卻是跨庫去操作的,甚至是跨物理機去操作的,那肯定性能就會經常莫名的下跌。

所以一般支持水平擴展性能的設計,哪怕是手工進行水平擴展的,一般都不會允許太多的多表關聯操作和約束,甚至表內join都需要盡量避免(免得給proxy產生集中衝擊)。

不過話又說回來,把邏輯層和存儲層解耦是個非常值得推薦的設計,同理可以避免很多存儲架構更新時遇到的問題朱元:阿里的編碼規範裡面寫超過三張表禁止join 這是為什麼?這樣的話那sql要怎麼寫?。當然可能也許你不會面對這樣的問題那就不詳談了。


在你真正理解範式之前,不要想著反範式。否則很容易陷入「迷信」的怪圈,自己並不知道為什麼要那麼做,只是人云亦云。如果應用場景發生了變化,那麼自己迷信的內容也許就不適用了。

所以原則上請先按照範式來設計,等你有一定的經驗以後,你自然能根據項目情況預估哪些地方可能會成為性能瓶頸,這樣也就可以憑經驗來使用反範式設計避開這些瓶頸。

最後說,離開具體的場景/用例談效率是扯淡的。有幾個答案無腦吹不用外鍵的,要麼是只談了某個場景下的,要麼是完全說空話。這樣碎片化的信息乍一看好像都有道理,但是實際上對你理解資料庫是沒有好處的。


自己寫程序約束,不然的話……

1. 項目發展得很好,請求量和數據量上漲,你發現資料庫的性能不夠,資料庫不能像無狀態的邏輯層一樣簡單擴容。好難過。。。

你可能會發現,外鍵約束檢查佔用了不少資料庫的資源。

你要分庫分表,分完之後,你發現數據都不在同一台機器上,這個外鍵約束不能滿足業務邏輯。

2.項目發展得不好,產品經理要瘋了,每天改幾次需求。你發現這個外鍵約束一會兒符合需求,一會兒不符合需求。難道每次都要改資料庫?

以上,都是我瞎說的。


對於許多應用情況來說,多數數據都是僅插入、不會刪除的。所以不用擔心外鍵失效的問題。而外鍵約束必然意味著 JOIN;JOIN 往往是資料庫中最慢的幾種操作之一。

實際上這種做法跟許多 NoSQL 的思路一樣:犧牲一致性換取性能。如果你對數據一致性要求高,數據存在刪除(導致曾經有效的外鍵可能失效)的情況,或者樂意用性能和靈活性(外鍵約束在許多變更 schema 的情況會很讓人痛恨)換取更多的安全感,使用外鍵也是正常的做法。


說一下我自己遇到的情況

大多數的刪除已經不是真正的刪除了。所以很多數據的檢查沒法依賴外鍵去做,還是得靠外部的代碼。

所以乾脆就把外鍵完全放棄,都用代碼去做

要不要使用外鍵應該是看業務上的需求吧


vczh和Pegasus Wang說的都很清楚了。

SQLServer、Oracle這些公司這麼多年的優化肯定不是白乾的。

最後的這個問題...這時不應該插入資料庫,算是bug。在提交留言時應當作數據檢查,然後提示A的狀態已被刪除。可能這個例子舉的不好。

反範式設計都是有設計原因和必要性的,不過很多人不管青紅皂白的直接使用不考慮場景...就很尷尬了。


性能要求高就不要使用外鍵,因為資料庫的擴展非常困難,不像APPserver可以隨便加加加


  1. 互聯網開發就是糙猛快,像Redis這種幾乎不提供任何ACID保證的存儲都能用得飛起,還要什麼外鍵約束?
  2. 外鍵不比主鍵,很多時候沒有外鍵約束並不會造成嚴重錯誤,而業務邏輯自己去保證外鍵約束也不難。
  3. 生產環境很多時候MySQL單庫單表是扛不住的,而分庫分表的情況下外鍵很難玩得動,更不論分散式系統為了提高擴展性和吞吐量,向來對一致性要求比較寬鬆。


推薦閱讀:

php+mysql開發的網站 如何使用hadoop+hbase+hive,能代替mysql么?
mysql 每次查詢一條數據查10次 和一次查詢10條數據效率有多少差距?
如何用C++介面備份MySQL的二進位數據?
附近的人怎麼計算出來的?
Stata 中文字元顯示成問號,該怎麼解決?

TAG:MySQL |