在資料庫中具體創建表結構的時候,需要創建外鍵嗎?

有的同事說,不創建外鍵,因為容易出錯.

我覺得只要表結構正確,為什麼會出錯呢?


非常同意劉鑫的答案。幾個月之前,關於要不要建外鍵我也疑惑過。也遇到了太多人,一上來就說,不要建外鍵,用業務層保證。

仔細研究了發現,幾乎你覺得凡是業務層必須要保證這個約束的,都應該在資料庫建立外鍵。這個約束在資料庫裡面就一個地方,在業務層可能分散在各個地方。假如你把這個約束當作是一個信息的話,那一個信息最好就放在一個地方。

正確性絕大部分時候比性能重要。我們公司的代碼的遺留代碼不少沒有外鍵,出現不少臟數據,浪費了很多人力經常清除臟數據。因為業務層的約束是非常不可靠的,業務層天天在變,公司每年都招新人,只要你的代碼審查不徹底,隨時可能帶來臟數據。有時用戶的真實數據髒了,你想清理都無法清理。

至於說性能的。說實話,大部分的公司的並發都沒有到阿里巴巴,twitter這個水平。一上來就說性能不好的,我只能說你想多了。彩票還沒有中,就在想中了以後那麼多錢該怎麼花。

解決並發的核心,根本不在資料庫有多快,而是緩存,使用如靜態化,內存緩存,redis,memcache這些緩存。另外還要使用消息隊列,將不需要當時反饋給用戶的任務排隊去做。

假如還沒有說服你,你就想想先加再放容易。開始就不加,那你這輩子幾乎就沒有機會了。

還看到過stackoverflow上面一個精彩的回答說: 外鍵約束有點像刷牙。媽媽天天跟你說要刷牙刷牙,你可以當耳邊風,而且短期也沒啥大問題。但是記住,笑的時候要注意了,別露出滿口黃牙。以後牙疼也別怪媽媽沒有提醒過你。

另外再吐槽一下。很多人不建外鍵的真實意圖就兩個字:懶+散。怕外鍵見了有約束,添加和刪除數據有順序依賴,不方便。喜歡不羈放縱愛自由,從來不帶tt, 把別人搞懷孕了就去墮胎,或者拍拍屁股就走了。這種人說用業務層保證,他說用命保證我都不信!

[推薦]

我當時迷惑的時候,看了不少這個帖子上面的回答,包括Fenng的回答: 大家設計資料庫時使用外鍵嗎? - MySQL 。裡面mysqlops的回答還是很不錯的。特別是他的回答有針對互聯網和傳統企業內的資料庫分開分析。

[補充]

在我看來,程序一般是: 正確性&>可讀性&>性能。使用外鍵約束就是選擇了正確性和可讀性,不使用,就是選擇了性能。你能理解我用Erstudio反向工程打開我們公司的ER圖的時候的反應嗎?除了後期建的5%的表(Erstudio會自動用線畫出外鍵關係),其他早期建立的每個表都是獨立的!!

對於那些用業務層保證的,我也能理解他們的做法。但是我希望說這話的人,是在非常了解資料庫的情況下說這句話的。像Fenng以前也說類似的話,我是認可他的做法的,對於阿里巴巴這樣的幾千幾萬的伺服器來說,炸出10%的性能提升都是值得的,都可以花很大的人力研究如何在業務層能夠嚴謹的保證這個約束。

而且真的有一天性能問題要求你放開外鍵,也可以分析那幾個表是瓶頸,根據2-8原則,只有20%的外鍵造成了了你80%的性能損耗。你只應該放開這20%的外鍵,同時要保證這部分的業務層沒有錯誤。

但是往往很多人說這句話的時候,沒有考慮到問問題的往往是小白。你說了一句用業務層保證的背後,已經有你對資料庫底層的各種思考,但是小白聽了就以為是聖旨。我們公司也跟我說「互聯網行業一般都不用外鍵的人「還跟我說過」能用left join就不要用inner join, 因為left join更快"...都是他不知從哪兒聽來的。


用不用外鍵,關鍵看業務需求。什麼需求呢?就是數據的一致性到底有沒有實時性的要求。

資料庫外鍵保證了資料庫里數據的「實時一致性」,用性能開銷帶來正確性保證。

而對於那種一致性要求不高的數據,可以用「延遲一致性」來保證。比如寫一個半夜跑的 Windows Service 來清理臟數據。

舉兩個栗子

博客系統就是那種數據一致性要求不高的應用。文章刪除了就刪除了,而它下面的那些評論立即刪掉和半夜(或者過幾天)刪掉,又有什麼差別呢?為何一定要拘泥於資料庫的外鍵,讓程序員先刪除評論,刪除評分,刪除圖片,刪除xxxx,然後才能夠刪除文章?

支付或者金融系統,每筆記錄都是錢,實時一致性要求很高,數據一定要正確,否則就會出現業務上不可接受的效果。

而事實上,實時一致性要求很高的系統,也一定會有後台服務再在合適的時候做一次驗證,來確認數據一致性的。

個人理解,僅供參考。


絕大多數書庫並不需要強制引用一致性,所以絕大多數時候意義不大。

當業務數據的引用一致性十分關鍵的時候,外鍵才有意義,比如有互生關係的表。


看應用,用得到就用,用不到不強折騰。外鍵本身就是個功能而已。想都不想就說外鍵有害那都是被MySQL社區的一些錯誤觀點給毒害了。


非常同意韓鎧的看法,拋開業務談資料庫和架構設計無異於空中樓閣,先要確保業務數據的正確性,不斷重構,優化架構才是王道


容易出錯是因為他總想違規刪除數據。。。


認真回答一下。

看評論區都在要討論外鍵性能的,那對性能到底有多少影響?我是做SQL Server的 一張圖告訴你

現在有A表,B表, 很簡單A表有個FKID是B表的ID,他們有外鍵關係。

以及C表什麼關係都沒,結構和A表一樣。

下圖為往A表和C表分別插數據。插入的時候 執行計劃如下:

紅圈是畫出來的

你可以直觀看到,有外鍵的表插入數據需要對外鍵表的索引進行SEEK,並且還要使用LOOP join合併結果,再過濾看是否報錯。

外鍵創建的越多JOIN的索引就越多。要判斷的次數就會更多。

所以它在確保證了數據完整性的同時付出的代價是很大的。

在高並發發生時對外檢表的索引需要保持只讀鎖。

需要額外的內存來進行JOIN(判斷有效性)

增加了IO讀(讀索引)

增加了CPU複核(檢查是否有效)

所以表上外鍵索引的SIZE越大,外鍵對性能的影響越大,外鍵個數越多,對性能影響越大。

這裡只是插入的開銷,同樣,此圖一樣用作更新,刪除的性能影響。

要不要建?考不考慮性能? 客觀自己判斷吧。


用資料庫保證一致性,簡單。用業務邏輯保證一致性,自由。

自己選擇平衡,沒有優劣沒有對錯。

小型的系統里,更多是用資料庫做的,可以分服可以隔離,少數幾台資料庫配置高一些,水平拆分一下就足夠了。

高壓力的系統里,業務邏輯處理的更多。比如兩個表級聯刪除的壓力一台高配的機器都扛不住的時候,只能放棄用外鍵。


表的外鍵是為了保證數據的完整性和一致性,不創建外鍵的話就必須在程序中保證數據的完整性,創建外鍵的話程序測試、調bug麻煩,自己選擇。


我也真是ORZ了,這麼明確的問題,居然評論里還有這麼多人說得言之鑿鑿。

直接先放答案:外鍵約束如果需要建,那就一定要在資料庫中建的,如果建了後出問題,那就說明這個外鍵根本就不需要建。

如果一個項目是真正遵循開發規範的話,那麼資料庫設計人員在資料庫設計階段就會明確需求,把這個事情做好,根本就沒有業務層的什麼事情。

換句話說,不建外鍵,由業務層來保證,是非常非常荒謬的!!!這就相當於你花大價錢買了輛汽車,但你出去旅遊時,只看中這個汽車的後備箱能放東西,然後你推著車走——這不是有病么?

從頭捋一下吧:

資料庫管理系統包含有數據存儲模塊,安全模塊,完整性模塊,並發模塊,恢復模塊等等,其中:

數據存儲模塊:就是存和取數據,表面上來看,也可以不用,在業務層用txt保存也沒有啥問題,但如果你的數據很多,如果你的存儲系統有多塊磁碟,還有SSD,甚至還有古老的磁帶,你就會覺得還是有這個模塊很不錯;

安全模塊:不僅是可以進行登錄時的檢查,還能夠控制許可權,比如學生這樣的角色只能查看成績,而教師可以修改成績這樣的需求,你也可以不用資料庫,直接在業務層實現;

完整性模塊:有主鍵約束,外鍵約束,用戶約束等等,當然你也可以不用,直接在業務層實現;因為題目就是講這個內容,我就多說點——所謂的完整性約束,就是給確保資料庫里的數據是符合規則的!比如說,我們有三個表,學生表,課程表,學生選課表,那麼學生選課表裡的數據,就要滿足下面的要求:學生必須是學生表裡面有的學生,所選的課程必須是課程表中有的課程,否則的話,裡面的數據就不可信。為了保證這一點,當你增加一條選課記錄,或者是刪除修改一個學生,或者是刪除修改一門課程的時候,都必須要檢查數據是不是符合要求,如果不符合要求,就不能操作。如果在資料庫中設置了外鍵,那麼資料庫會來做這個檢查過程,如果沒有設置,那就是所謂的業務層來做,業務層來做沒有絲毫的好處,反倒是一大堆的問題:1)要自己來寫程序做這個檢查,耗時間;2)有可能會出bug,效率還不會高;3)要檢測到每一種可能影響數據完整性的操作,有一種沒有檢查到就會出問題;4)要確保每一個操作數據的程序員都深刻理解這些要求並進行檢查,有一個人作死就總體崩潰(這一點恰恰是最難做到的,你們可以腦補一下不同公司的不同程序員在不同時期操作同一個表數據的撕B過程)。

並發模塊:主要就是解決當有多個用戶來同時訪問相同或不同的數據時,如何保證不會產生錯誤,當然你也可以不用,直接在業務層實現;

恢復模塊:資料庫伺服器在運行的時候,有很多的業務,比如A向B轉賬1000塊,這裡面資料庫要做兩件事情,一是A賬上少1000塊,二是B賬上加1000塊,如果這兩個操作過程中正好出現問題,比如停電,比如操作系統藍屏重啟等等 ,那就會導致問題,有了恢復模塊,你只需要重啟機器,問題就解決了,爽不爽?當然,你也可以不用,直接在業務層去檢測這類問題並實現;

舉個主鍵約束的例子:當年我們還在讀碩士的時候,給學校教務處做選課系統,在學生選課表裡面,學號+課程是主鍵,但我們嫌麻煩,就沒有設,直接在業務層實現——如果001號學生選了課程A,就去資料庫裡面查找一下,如果已經有了記錄,就彈出對話框說已經選了,不允許再選,這也運行得非常好,可是在某一天,有個哥們在備份數據的時候,把源表和目的表都選了同樣的「學生選課表」,操作完後他發現沒有反應,於是又選了部分數據再操作了一遍,這次他終於發現有問題了,但這個表中有的記錄重複了四次,有的重複了三次,有的重複了兩次,而我們計算學分的代碼就是讀學生選課表的數據,結果有的學生畢業的時候一看,學分都有上千個,最後只好又去改代碼,去掉重複的選項,搞得一地雞毛……

最後再總結一下:花了大價錢或者大精力配置好了資料庫管理系統,就應該要能明白它能做什麼,要能充分利用資料庫管理系統的功能,因為它的功能是經過了千錘百鍊,並且在絕大多數情況下都比你自己寫的代碼要健壯,要完善,要效率高,最重要的是還節省了你的時間和精力。還是那個例子,你如果只是想裝點東西推著走,一個皮箱就足夠,但如果這點需求就買輛汽車,把東西丟到後備箱然後再推著走,呃,你開心就好……


具體要看需求.

多數情況下不使用外鍵確實方便.但如果業務上真的需要,也沒得選.


我行工作經驗表明,第一不準建外鍵,第二不準使用觸發器,第三不準隨便建點陣圖索引。


一般是要創建的。

雖然DDD強調以領域邏輯為核心,要在業務層保證數據一致性,但是這和創建外鍵約束並不矛盾。畢竟,數據對於一個企業是最重要的資產,而應用層會變化,比如從遷移到不同的語言平台。如果所有約束信息都保留在應用層,難免這些知識會丟失。


當然不建了。業務邏輯變了怎麼辦?,而且外鍵根本不能滿足參雜了業務邏輯的數據關係。


分事務型和倉庫型兩種情況。

對於事務型資料庫,必須用起來,因為寫應用程序的人是無法保證數據的完整性的。一個大系統在沒有外鍵的情況下跑一段時間,等到你寫統計分析的應用時,會發現大量的垃圾數據,讓你痛不欲生。如果用這些數據建倉庫,需要巨量的數據清洗工作。

對數據倉庫而言,主要目的是為了查詢,自己也不產生數據,外鍵的功能無用武之處,因此基本上沒必要建外鍵。


推薦閱讀:

碼農學CFA有價值么?
如何融入工作?如何看待工作的意義?
程序員站立工作是種什麼體驗?怎麼樣構建工作環境?
一個開發一年能為公司創造多少利潤?
有哪些可以送給碼農的春聯?

TAG:資料庫 | 程序員 | 編程 | 軟體架構 | 架構師 |