為什么數(shù)據(jù)庫(kù)不應(yīng)該使用外鍵?

劇照 :女王的棋局
作者:Draveness
來(lái)源:真沒(méi)什么邏輯

ALTER TABLE postsADD CONSTRAINT FOREIGN KEY (author_id)REFERENCES authors(id);
posts?表中存在?author_id?字段。從 SQL 語(yǔ)句中的?CONSTRAINT?關(guān)鍵字我們也能推測(cè)出外鍵不是一種數(shù)據(jù)類型,它是不同關(guān)系表之間的約束。
RESTRICT、CASCADE?和?SET NULL?等幾種[^4],當(dāng)我們?yōu)殛P(guān)系表中的字段增加外鍵約束時(shí),需要指定外鍵的類型,最常見(jiàn)的也就是?RESTRICT?和?CASCADE?兩種,其中?RESTRICT?為外鍵的默認(rèn)類型,不同類型的外鍵會(huì)帶來(lái)不同的額外開(kāi)銷,而這些額外開(kāi)銷就是我們不使用外鍵的理由:使用? RESTRICT?會(huì)在更新或者刪除記錄時(shí)對(duì)外鍵對(duì)應(yīng)的記錄是否存在進(jìn)行一致性檢查;使用? CASCADE?會(huì)在更新或者刪除記錄時(shí)觸發(fā)級(jí)聯(lián)更新或者刪除操作;
注意:MySQL 中的? NO ACTION?和?RESTRICT?具有相同的語(yǔ)義[^5]。
一致性檢查
RESTRICT?時(shí),在創(chuàng)建、修改或者刪除記錄時(shí)都會(huì)檢查引用的合法性。想要在 MySQL 等數(shù)據(jù)庫(kù)中觸發(fā)外鍵的一致性檢查其實(shí)非常容易,假設(shè)我們的數(shù)據(jù)庫(kù)中包含?posts(id, author_id, content)?和?authors(id, name)?兩張表,在執(zhí)行如下所示的操作時(shí)都會(huì)觸發(fā)數(shù)據(jù)庫(kù)對(duì)外鍵的檢查:向? posts?表中插入數(shù)據(jù)時(shí),檢查?author_id?是否在?authors?表中存在;修改? posts?表中的數(shù)據(jù)時(shí),檢查?author_id?是否在?authors?表中存在;刪除? authors?表中的數(shù)據(jù)時(shí),檢查?posts?中是否存在引用當(dāng)前記錄的外鍵;
authors、posts?和?foreign_key_posts?三種表,如下所示,其中?posts?和?foreign_key_posts?兩個(gè)表中的列完全相同,只是?foreign_key_posts?表為?author_id?字段增加了?RESTRICT?類型的外鍵約束:
authors?表中插入一條記錄,隨后分別在?posts?和?foreign_key_posts?中插入多條新數(shù)據(jù)列引用該條記錄,前者不會(huì)檢查外鍵的合法性,而后者會(huì)做額外的檢查。你可以在 這里 找到作者用來(lái)測(cè)試外鍵額外開(kāi)銷的 Go 語(yǔ)言代碼[^6],經(jīng)過(guò)多次基準(zhǔn)測(cè)試,我們可以得到如下所示的結(jié)果:BenchmarkBaseline-8 3770 309503 ns/opBenchmarkForeignKey-8 3331 317162 ns/opBenchmarkBaseline-8 3192 315506 ns/opBenchmarkForeignKey-8 3381 315577 ns/opBenchmarkBaseline-8 3298 312761 ns/opBenchmarkForeignKey-8 3829 345342 ns/opBenchmarkBaseline-8 3753 291642 ns/opBenchmarkForeignKey-8 3948 325239 ns/op
向表中插入數(shù)據(jù)或者修改表中的數(shù)據(jù)時(shí),都應(yīng)該執(zhí)行額外的? SELECT?語(yǔ)句確保它引用的數(shù)據(jù)在數(shù)據(jù)庫(kù)中存在;在刪除數(shù)據(jù)之前需要執(zhí)行額外的? SELECT?語(yǔ)句檢查是否存在當(dāng)前記錄的引用;
posts?表中插入或者修改數(shù)據(jù)時(shí),需要的處理相對(duì)比較簡(jiǎn)單,我們只需要執(zhí)行有限的?SELECT?語(yǔ)句并按照如下所示的模式執(zhí)行對(duì)應(yīng)的操作就可以了:BEGINSELECT * FROM authors WHERE id =FOR UPDATE; -- INSERT INTO posts ... / UPDATE posts ...END
authors?表中的數(shù)據(jù),就需要查詢所有引用?authors?數(shù)據(jù)的表;如果有 10 個(gè)表都有指向?authors?表的外鍵,我們就需要在 10 個(gè)表中查詢是否存在對(duì)應(yīng)的記錄,這個(gè)過(guò)程相對(duì)比較麻煩,不過(guò)也是為了實(shí)現(xiàn)完整性的必要代價(jià),不過(guò)這種模擬外鍵方法其實(shí)遠(yuǎn)比使用外鍵更消耗資源,它不僅需要查詢關(guān)聯(lián)數(shù)據(jù),還要通過(guò)網(wǎng)絡(luò)發(fā)送更多的數(shù)據(jù)包。級(jí)聯(lián)操作
CASCADE?行為,那么在客戶端更新或者刪除數(shù)據(jù)時(shí)就會(huì)觸發(fā)級(jí)聯(lián)操作:ALTER TABLE postsADD CONSTRAINT FOREIGN KEY (author_id)REFERENCES authors(id)ON UPDATE CASCADEON DELETE CASCADE;
當(dāng)客戶端更新? authors?表中記錄的主鍵時(shí),數(shù)據(jù)庫(kù)會(huì)同時(shí)更新?posts?表中所有引用該記錄的外鍵;當(dāng)客戶端刪除? authors?表中的記錄時(shí),數(shù)據(jù)庫(kù)會(huì)刪除所有與?authors?表關(guān)聯(lián)的記錄;
RESTRICT?行為一樣,所有外鍵的更新和刪除行為都可以通過(guò)執(zhí)行額外的檢查和操作保證數(shù)據(jù)的一致。
authos?表中的數(shù)據(jù)時(shí),如果我們同時(shí)在?authors?和?posts?中指定了級(jí)聯(lián)刪除的行為,那么數(shù)據(jù)庫(kù)會(huì)同時(shí)刪除所有關(guān)聯(lián)的?posts?記錄以及與?posts?表關(guān)聯(lián)的?comments?數(shù)據(jù)。CASCADE?行為,這對(duì)于保證數(shù)據(jù)庫(kù)中數(shù)據(jù)的合法性有著很重要的意義,使用該特性可以避免數(shù)據(jù)庫(kù)中出現(xiàn)過(guò)期的、不合法的數(shù)據(jù),但是在使用時(shí)也要合理預(yù)估可能造成的最壞情況。DELETE FROM posts WHERE author_id = 1 LIMIT 100;DELETE FROM posts WHERE author_id = 1 LIMIT 100;...DELETE FROM authors WHERE id = 1;
CASCADE?相比,這種方式會(huì)帶來(lái)更大的額外開(kāi)銷,只是我們能降低對(duì)數(shù)據(jù)庫(kù)性能的瞬時(shí)影響。總結(jié)
不使用外鍵犧牲了數(shù)據(jù)庫(kù)中數(shù)據(jù)的一致性,但是卻能夠減少數(shù)據(jù)庫(kù)的負(fù)載; 模擬外鍵將一部分工作移到了數(shù)據(jù)庫(kù)之外,我們可能需要放棄一部分一致性以獲得更高的可用性,但是為了這部分可用性,我們會(huì)付出更多的研發(fā)與維護(hù)成本,也增加了與數(shù)據(jù)庫(kù)之間的網(wǎng)絡(luò)通信次數(shù); 使用外鍵保證了數(shù)據(jù)庫(kù)中數(shù)據(jù)的一致性,也將全部的計(jì)算任務(wù)全部交給了數(shù)據(jù)庫(kù);
RESTRICT?外鍵會(huì)在更新和刪除關(guān)系表中的數(shù)據(jù)時(shí)對(duì)外鍵約束的合法性進(jìn)行檢查,保證外鍵不會(huì)引用到不存在的記錄;CASCADE?外鍵會(huì)在更新和刪除關(guān)系表中的數(shù)據(jù)時(shí)觸發(fā)對(duì)關(guān)聯(lián)記錄的更新和刪除,在數(shù)據(jù)量較大的數(shù)據(jù)庫(kù)中可能會(huì)有數(shù)量級(jí)的放大效果;
數(shù)據(jù)庫(kù)中還有哪些特性是我們?cè)谏a(chǎn)環(huán)境中不會(huì)使用的?為什么? 分布式的關(guān)系型數(shù)據(jù)庫(kù)與 MySQL 等傳統(tǒng)數(shù)據(jù)庫(kù)有哪些區(qū)別?

近期熱門文章推薦:

評(píng)論
圖片
表情
