大家設計資料庫時使用外鍵嗎?

儘管在討論資料庫時知道表與表之間有外鍵關係, 但是具體到表定義就省略掉外鍵定義, 而選擇在應用層管理相關的外鍵邏輯. 只因為聽說外鍵性能不好, 或者說有外鍵後表與表之間太耦合了, update, delete等操作都會涉及相關的表.


先評論下各位專家回答的內容:
@feng:一個DBA是否有足夠的設計能力,就靠他有多大的能力做反範式設計就可以了
--- 這句話有點過於放大,以及跟作者說的題目無半毛錢的關係,是否使用外鍵跟範式無任何關係

@西祠響馬 --- 說的內容是有道理的
@光芊源 --- 提到性能


從上到下太多人提到範式,甚至強調反範式 ---- 你們懂範式嘛? 範式跟外鍵的設計有關係嗎?
別不懂裝懂了,各位專家,誤人子弟就不好!大家若不懂範式,建議看下 薩老和王美女主編的書籍&<&<資料庫系統概論&>&>

回答下問題:
外鍵是否採用看業務應用場景,以及開發成本的,大致列下什麼時候適合,什麼時候不適合使用:

1. 互聯網行業應用不推薦使用外鍵: 用戶量大,並發度高,為此資料庫伺服器很容易成為性能瓶頸,尤其受IO能力限制,且不能輕易地水平擴展;若是把數據一致性的控制放到事務中,也即讓應用伺服器承擔此部分的壓力,而引用伺服器一般都是可以做到輕鬆地水平的伸縮;

2.傳統行業
1&>.軟體應用的人數有限,換句話說是可控的;
2&>.資料庫伺服器的數據量也一般不會超大,且活躍數據有限;

綜合上述2句話描述,也即資料庫伺服器的性能不是問題,所以不用過多考慮性能的問題;另外,使用外鍵可以降低開發成本,藉助資料庫產品自身的觸發器可以實現表與關聯表之間的數據一致性和更新;最後一點,使用外鍵的方式,還可以做到開發人員和資料庫設計人員的分工,可以為程序員承擔更多的工作量;

為何說外鍵有性能問題:
1.資料庫需要維護外鍵的內部管理;
2.外鍵等於把數據的一致性事務實現,全部交給資料庫伺服器完成;
3.有了外鍵,當做一些涉及外鍵欄位的增,刪,更新操作之後,需要觸發相關操作去檢查,而不得不消耗資源;
4.外鍵還會因為需要請求對其他表內部加鎖而容易出現死鎖情況;


資料庫的諸多設計,帳號,許可權,約束,觸發器,都是為 C/S 結構設計的,是以 C 端不可信做為假設前提的。B/S 模式安全邊界前移到 web 服務層,應用與資料庫之間是可信的,應用自行完成這些功能更加靈活。

所以能不用就不用。


怎麼總有人問我資料庫的問題...

透露一個秘密,我在前公司的時候摘掉了所有的外鍵。業務約束通過中間層控制。面向Web的應用應該這樣。

一個DBA是否有足夠的設計能力,就看他有多大的能力做反範式設計就可以了。不要問為什麼。


【update, delete等操作都會涉及相關的表】什麼的,這是需求的一部分啊,不是你從C/S改成B/S就可以不算的。


我舉個簡單的例子,我table1存學生的信息,另table2存學生沒一次考試的信息,那table2的id就是指向table1的foreign key,沒錯吧。這個時候你把學生x的信息刪掉了,你所說的不希望【update, delete等操作都會涉及相關的表】的意思就是,他的成績我還留著!


這有兩個後果

1:萬一你的新人的id給你重用了,你的資料庫就毀了

2:長此以往,垃圾數據一大堆啊

所以一定要讓你在刪除學生的時候給你當頭一棒,告訴你要先刪成績,再刪人。這是多麼合理的做法。


有的同學一定要說,那我就是不注意,崩了老闆抓我扣工資怎麼辦啊——資料庫的這個行為又不是隨機行為,你上線前就不先測試一下?學bumble bee嘛?


我想,這取決於資料庫的用途、規模、架構,有外鍵,可以提高魯棒性、健壯性,但是約束檢驗顯然會拖慢速度。
規模上說,數據量大的不適合用外鍵,小的可以用;用途上安全性、可靠性很重要的就要用外鍵,否則可以不用。具體情況具體解決了,因為也有矛盾的時候,數據量極大,但是又要求高可靠,例如銀行金融、晶元生產等,仍然需要外鍵的存在。可以通過SAN+RAID等硬體提升解決矛盾。
要求高並發的情況下,並不適合外鍵,有的連關係資料庫都不用了,甚至資料庫都不用了。

這類問題真沒有絕對的答案,什麼情況下該怎樣做,只能是多想,多做了,錯的多了,就懂了。


因為很多答案對但是不精到,我再回答一個。

這實際上不是個資料庫問題,而是個架構問題。

使用外鍵與否,看你使用的框架是否有足夠的事務管理能力。
(其實不僅限於外鍵,還有一切可被替代的 db 高級特性)

如果足夠,那麼應該盡量避免。

理由很簡單:app server 比 db server 便宜,而且拓展容易。


我簡單地畫了一個最常見的中型網路服務的架構示意圖:

用戶的請求,通過前端的負載均衡分發到應用伺服器上,應用伺服器對於數據的請求,又通過數據的負載均衡分發的讀寫分離的資料庫集群上(讀寫分離的從資料庫通過二進位日誌從主伺服器同步數據)。
這種架構,對於系統開發的複雜度不會有什麼明顯的提升。

但是很容易可以注意到,這個架構下,負責寫的資料庫伺服器,只有一個。如果用綠色圓餅代表伺服器負載的話,加入約束的主伺服器,insert、update、delete操作都會有更多的性能消耗。於是負責寫入的主資料庫伺服器會很快遇到瓶頸。

根據木桶理論,我們可以知道,這個系統也遇到了瓶頸。

那麼把負載最高的伺服器上的乾的活,轉移到易於擴展的其他伺服器上,可以有效提升整個系統的性能。

那麼把 主資料庫伺服器(寫) 上的約束轉移到應用伺服器上去做,就可以有效提升性能了——極端點的能轉移就全部轉移。

還有為了壓榨 主資料庫伺服器(寫) 上的一些性能,用GUID替代AutoIncrement作為主鍵的唯一性保障,同樣也是這個道理。

這麼做之後,我們可以觀察到 主資料庫伺服器(寫) 上的負載有了顯著的降低,但是應用伺服器的負載提升了,這時候怎麼辦呢?只要再加幾台應用伺服器就好了。

最後,再分享另一種的網路服務的系統架構:

這種情況下,把約束做到資料庫里,就很方便了,降低了應用服務上邏輯層的複雜度。(有人要問了:為什麼負載這麼低啊?因為用戶量少啊hhhhhhhhhh)

總之,加不加,還是看需求。
但是我個人傾向於把對性能的需求,放到易於擴展伺服器迅速解決性能瓶頸的服務中。


學院派告訴你建表的時候必須使用鍵,而實踐派除了主鍵和非空約束外,其他的都不要。主要通過程序來控制外鍵關聯!


我的個人經驗:
1. 利用ER設計工具,在「設計」時盡量的「正規化」(包括所謂的外鍵及其他資料庫約束)。
? 你在這時不按範式設計,不考慮數據約束,那你在「中間層」,「業務層」控制什麼咧?
2. 提升第ER設計的抽象層次(有本《數據模型資源手冊》可供參考)。
3. 再檢視一遍做好的ER設計。
? 我個人經常會從「正規化」好的ER模型中發現新靈感。
4. 根據其他因素考慮在哪些地方做「反正規劃」。
5. 根據具體情況考慮把各種數據約束放在資料庫里還是提出來。
6. 物理實現。

我做的不是互聯網開發,也不是DBA,上述經驗也不大成體系。
我相信Fenng應該是高手,不過這次的回答不大成體系。

另外不知道為什麼底下有人莫名其妙的頂,不知是一個小圈子的人互相捧呢,還是偶像崇拜?


很少使用外鍵,因為外鍵雖然增加了一致性,但是會減弱性能。
雖然現在因為磁碟廉價,所以大家都提倡反範式,但是不明白為什麼要反,為什麼不能問為什麼?不搞清楚,跟著瞎反,到後面會發現資料庫性能超級差。
可以反範式,但是一定要明白為什麼反,很多時候反是為了增加性能,假如不能增加性能,反而會減弱性能那為什麼要反呢


資料庫也是程序,不是一個完全的黑盒子。
上面所提到的性能問題,比如用應用層控制一致性啊,和資料庫分離啊,之類的說法是基於把資料庫作為一個黑盒子來說的,其實資料庫的各個部分(存儲,一致性,查詢優化等)都是能夠拆分部署的。
在Oracle(偏向一致性)和DB2(偏向並行和查詢速度)的集群中,這些性能問題都能得到很好的解決。開源資料庫中基於postgres的greenplum也是一個比較典型的解決方案。

所以非常不支持針對有一致性要求的場合下進行反範式設計,除非對於分散式一致性的處理的經驗,已經能達到oracle/ibm/微軟等企業的水平,畢竟資料庫畢竟也是開發出來的程序而已。
如果沒有相關經驗或者不達標,研發也是有成本的(包括因為經驗不足導致的失敗或者加班修補等),大多數時候還不如購買商業資料庫省錢。

如果對一致性沒有要求,或者後期修補也可以接受,那才考慮反範式設計,互聯網上很多系統其實可以可以歸納為內容展示系統,這些系統對一致性沒有強烈的要求,可以反範式甚至是基於各種新潮的技術(其實大部分就是個緩存,以失去一致性為代價),來提高性能。

不得不提一個事情,如果對業務的理解以及業務本身還是個小鳥,就不要想它變成大鳥後會怎樣,這樣只會浪費時間在沒用的地方,去鑽研所謂的高級技術,集群啊,分散式啊等等。以資料庫為核心的設計(包括存儲過程,並且在有一致性要求的地方嚴格遵守範式,並使用觸發器完成複雜的一致性處理),適合大部分信息系統。

最後再分享一個經驗,mysql對於DDL沒有去保證一致性,所以如果系統功能比較複雜,特別是組態類型的系統(數據表、觸發器、約束等是動態創建的),應盡量避免使用mysql,開源的postgres更能符合需求,或者使用免費/收費的商業資料庫。


測試環境約束,生產環境去掉。


新手一枚,自己搞外包成長起來的,來說說看法,務噴。

之前搞一個外包是開發OA系統的,在系統設計之初,資料庫的設計很規範的,大量使用外鍵,約束,符合範式要求。因為這樣,我可以迅速的建立一個可靠性非常高的資料庫結構,而不用讓應用程序層去做過多的檢查,誰都想寫最少的代碼,完成更多的功能。

所以,項目初始階段,使用外鍵是必須的,而且是強烈推薦使用外鍵,資料庫自帶的約束,這樣可以讓你的業務架構迅速成型。

等項目的數據量越做越大,用戶數越來越多的時候,那個時候,已經可以充分證明你的業務架構是正確的,這個時候你要是有性能瓶頸上的問題,完全可以把外鍵去除,轉移到應用層實現。


總結:和我一樣是新手的同學,不要被老司機的結論所矇騙,他們一般都只說結果,不講過程的。

過程就是,設計業務架構,系統初始之初,規規矩矩的使用外鍵,符合範式,待業務發展,確定架構本身沒有問題,一些約束漸漸的平移到應用層實現。

結果就是,每個階段都有不同的做法。根據實際情況去設計實現。


外鍵在早期企業系統資料庫設計裡面比較多吧,本意是好了,幫程序員節省delete,update操作,實際上增加了潛規則,也增加了軟體複雜度。互聯網應用中,一般流量比較大,資料庫當memecache用,大表+冗餘欄位,索引還建在外部用sphinx之類,基本上沒有表和表的關聯關係,外鍵無用


雖然是挖墳,但是還是說一下,就當是自己寫筆記:
設計資料庫時使用外鍵關聯,甚至等到系統部署發布,在生產環境上仍然使用外鍵。。。
畢竟,等到你發現用戶量增長,資料庫寫入成為瓶頸的時候,完全可以刪掉外鍵關聯嘛。。。
而且,因為之前的外鍵約束,也能儘早的發現應用系統的 Bug,從而在之後刪掉外鍵關聯的時候不用擔心應用系統哪裡做得不到位。。。

當然,如果用戶量增長過快,或者是在一個用戶量本身就很大的舊系統上新增業務功能,那就只能不用外鍵,老老實實審查代碼了。。。


邏輯上有,但從來不真正建立。
請問使用外鍵更好的場景是哪些?它的好處主要是由資料庫自身來控制數據完整性和一致性,方便的是開發人員,苦的是資料庫伺服器,那處在DBA立場,有什麼理由使用它?


外鍵之類的約束還有一個作用是防新人啊
有些新人是能直接在資料庫里insert和delete,而且總是插些不規範的數據進去
什麼業務架構都防不住


樓上各種大神還是沒說,如果不用外鍵,怎麼保證數據的一致性= =


不用,這些約束交給應用完成。盡量不讓DB成為瓶頸


做外鍵主要目的就是鏈接各表間的關係,防止直接操作導致數據紊亂,或者垃圾信息的產生。但目前各種後端框架發展到目前這種地步,在模型層已經可以處理好這些邏輯了,而模型層的意義本來就是避免對資料庫進行直接操作。我目前在設計資料庫的時候基本都不設置外鍵,而是在模型層刪除處做檢查處理,邏輯特別清晰。


推薦閱讀:

如何MySQL鏈接Excel,實現一個簡易的自動更新報表?
MySQL主資料庫負責增刪改,從資料庫負責增改怎麼配置?
使用ECharts如何從mysql資料庫取數據顯示在頁面?

TAG:SQL | MySQL | 資料庫設計 | 外鍵 |