數(shù)據(jù)庫的唯一標(biāo)示符(ID)的選擇

背景:數(shù)年的工作中,已經(jīng)設(shè)計(jì)了很多系統(tǒng)或產(chǎn)品的數(shù)據(jù)庫,有單機(jī)的、有局域網(wǎng)環(huán)境下的、也有互聯(lián)網(wǎng)環(huán)境下的,對于不同的環(huán)境,設(shè)計(jì)考慮都有所不同。即使對于相同的環(huán)境,也會因?yàn)闃I(yè)務(wù)或者數(shù)據(jù)量的不同而有不同的設(shè)計(jì)。近期,又要設(shè)計(jì)一款互聯(lián)網(wǎng)產(chǎn)品的數(shù)據(jù)庫(MySQL服務(wù))。經(jīng)過之前的積累,在表的ID設(shè)計(jì)這個環(huán)節(jié)就進(jìn)行了大量的分析、比較、學(xué)習(xí),對ID的設(shè)計(jì)也有了更系統(tǒng)和深刻的認(rèn)知,把自己學(xué)習(xí)實(shí)踐到的知識總結(jié)下來,分享給大家。

創(chuàng)新互聯(lián)建站是一家專注于成都網(wǎng)站制作、做網(wǎng)站、外貿(mào)營銷網(wǎng)站建設(shè)成都服務(wù)器托管的網(wǎng)絡(luò)公司,有著豐富的建站經(jīng)驗(yàn)和案例。


主鍵id的選擇

對于關(guān)系數(shù)據(jù)庫來說,設(shè)計(jì)每個表的第一步都會確定其主鍵,主鍵就是ID。在“常識”之中,int型的自增id,字符串類型的uuid,其他與業(yè)務(wù)相關(guān)的唯一鍵…都是我們作為主鍵的選擇。那么是不是說在一張表中只要能保證值唯一的屬性列都可以做為主鍵或者更合適做主鍵呢?

那我們首先清晰幾個概念:

  • 邏輯主鍵(代理主鍵):在數(shù)據(jù)庫表中采用一個與當(dāng)前表中業(yè)務(wù)邏輯信息無關(guān)的字段作為其主鍵,或稱為“偽主鍵”;

  • 業(yè)務(wù)主鍵(自然主鍵):在數(shù)據(jù)庫表中把具有業(yè)務(wù)邏輯含義的字段作為主鍵;

舉一個很常見的例子:一張用戶信息表,列屬性有id、用戶名、手機(jī)號…,其中用戶名和手機(jī)號(作為登錄賬號二者都是唯一的)。其中id便可作為邏輯主鍵,用戶名和手機(jī)號都可以作為業(yè)務(wù)主鍵。那我是不是可以隨便選一個,甚至我選擇了業(yè)務(wù)主鍵都可以不要邏輯主鍵?

那么我們首先來看看邏輯主鍵和業(yè)務(wù)主鍵之間紛紛烈烈的觀點(diǎn)分歧:

  • 支持邏輯主鍵

    表通過主鍵來保證每條記錄的唯一性,表的主鍵應(yīng)當(dāng)不具有任何業(yè)務(wù)含義,因?yàn)槿魏斡袠I(yè)務(wù)含義的列都有改變的可能性。關(guān)系數(shù)據(jù)庫學(xué)的最重要的一個理論就是:不要給關(guān)鍵字賦予任何業(yè)務(wù)意義。假如關(guān)鍵字具有了業(yè)務(wù)意義,當(dāng)用戶決定改變業(yè)務(wù)含義,也許他們想要為關(guān)鍵字增加幾位數(shù)字或把數(shù)字改為字母,那么就必須修改相關(guān)的關(guān)鍵字。一個表中的主關(guān)鍵字有可能被其他表作為外鍵。就算是一個簡單的改變,譬如在客戶號碼上增加一位數(shù)字,也可能會造成極大的維護(hù)上的開銷。

    為了使表的主鍵不具有任何業(yè)務(wù)含義,一種解決方法是使用代理主鍵,例如為表定義一個不具有任何業(yè)務(wù)含義的ID字段(也可以叫其他的名字),專門作為表的主鍵。
    ——孫衛(wèi)琴《精通Hibernate:Java對象持久化技術(shù)詳解》P8


  • 使用邏輯主鍵的主要原因是,業(yè)務(wù)主鍵一旦改變則系統(tǒng)中關(guān)聯(lián)該主鍵的部分的修改將會是不可避免的,并且引用越多改動越大。而使用邏輯主鍵則只需要修改相應(yīng)的業(yè)務(wù)主鍵相關(guān)的業(yè)務(wù)邏輯即可,減少了因?yàn)闃I(yè)務(wù)主鍵相關(guān)改變對系統(tǒng)的影響范圍。業(yè)務(wù)邏輯的改變是不可避免的,因?yàn)椤坝肋h(yuǎn)不變的是變化”,沒有任何一個公司是一成不變的,沒有任何一個業(yè)務(wù)是永遠(yuǎn)不變的。最典型的例子就是***升位和駕駛執(zhí)照號換用***號的業(yè)務(wù)變更。而且現(xiàn)實(shí)中也確實(shí)出現(xiàn)了***號碼重復(fù)的情況,這樣如果用***號碼作為主鍵也帶來了難以處理的情況。當(dāng)然應(yīng)對改變,可以有很多解決方案,方案之一是做一新系統(tǒng)與時俱進(jìn),這對軟件公司來說確實(shí)是件好事。

    使用邏輯主鍵的另外一個原因是,業(yè)務(wù)主鍵過大,不利于傳輸、處理和存儲。我認(rèn)為一般如果業(yè)務(wù)主鍵超過8字節(jié)就應(yīng)該考慮使用邏輯主鍵了,因?yàn)閕nt是4字節(jié)的,bigint是8字節(jié)的,而業(yè)務(wù)主鍵一般是字符串,同樣是 8 字節(jié)的 bigint 和 8 字節(jié)的字符串在傳輸和處理上自然是 bigint 效率更高一些。想象一下 id 為”12345678” 和 id為12345678 的匯編碼的不同就知道了。當(dāng)然邏輯主鍵不一定是 int 或者 bigint ,而業(yè)務(wù)主鍵也不一定是字符串也可以是 int 或 datetime 等類型,同時傳輸?shù)囊膊灰欢ň褪侵麈I,這個就要具體分析了,但是原理類似,這里只是討論通常情況。同時如果其他表需要引用該主鍵的話,也需要存儲該主鍵,那么這個存儲空間的開銷也是不一樣的。而且這些表的這個引用字段通常就是外鍵,或者通常也會建索引方便查找,這樣也會造成存儲空間的開銷的不同,這也是需要具體分析的。

    使用邏輯主鍵的再一個原因是,使用 int 或者 bigint 作為外鍵進(jìn)行聯(lián)接查詢,性能會比以字符串作為外鍵進(jìn)行聯(lián)接查詢快。原理和上面的類似,這里不再重復(fù)。

    使用邏輯主鍵的再一個原因是,存在用戶或維護(hù)人員誤錄入數(shù)據(jù)到業(yè)務(wù)主鍵中的問題。例如錯把 RMB 錄入為 RXB ,相關(guān)的引用都是引用了錯誤的數(shù)據(jù),一旦需要修改則非常麻煩。如果使用邏輯主鍵則問題很好解決,如果使用業(yè)務(wù)主鍵則會影響到其他表的外鍵數(shù)據(jù),當(dāng)然也可以通過級聯(lián)更新方式解決,但是不是所有都能級聯(lián)得了的。
    ——SwitchBlade的總結(jié)

  • 支持業(yè)務(wù)主鍵

    如果你的表中包含一列能確保唯一、非空以及能夠用來定位一條記錄,就別僅僅因?yàn)閭鹘y(tǒng)而覺得有必要再加上一個偽主鍵。
    ——Bill Karwin 《SQL反模式》 p41


  • 使用業(yè)務(wù)主鍵的主要原因是,增加邏輯主鍵就是增加了一個業(yè)務(wù)無關(guān)的字段,而用戶通常都是對于業(yè)務(wù)相關(guān)的字段進(jìn)行查找(比如員工的工號,書本的 ISBN No. ),這樣我們除了為邏輯主鍵加索引,還必須為這些業(yè)務(wù)字段加索引,這樣數(shù)據(jù)庫的性能就會下降,而且也增加了存儲空間的開銷。所以對于業(yè)務(wù)上確實(shí)不常改變的基礎(chǔ)數(shù)據(jù)而言,使用業(yè)務(wù)主鍵不失是一個比較好的選擇。另一方面,對于基礎(chǔ)數(shù)據(jù)而言,一般的增、刪、改都比較少,所以這部分的開銷也不會太多,而如果這時候?qū)τ跇I(yè)務(wù)邏輯的改變有擔(dān)憂的話,也是可以考慮使用邏輯主鍵的,這就需要具體問題具體分析了。

    使用業(yè)務(wù)主鍵的另外一個原因是,對于用戶操作而言,都是通過業(yè)務(wù)字段進(jìn)行的,所以在這些情況下,如果使用邏輯主鍵的話,必須要多做一次映射轉(zhuǎn)換的動作。我認(rèn)為這種擔(dān)心是多余的,直接使用業(yè)務(wù)主鍵查詢就能得到結(jié)果,根本不用管邏輯主鍵,除非業(yè)務(wù)主鍵本身就不唯一。另外,如果在設(shè)計(jì)的時候就考慮使用邏輯主鍵的話,編碼的時候也是會以主鍵為主進(jìn)行處理的,在系統(tǒng)內(nèi)部傳輸、處理和存儲都是相同的主鍵,不存在轉(zhuǎn)換問題。除非現(xiàn)有系統(tǒng)是使用業(yè)務(wù)主鍵,要把現(xiàn)有系統(tǒng)改成使用邏輯主鍵,這種情況才會存在轉(zhuǎn)換問題。暫時沒有想到還有什么場景是存在這樣的轉(zhuǎn)換的。

    使用業(yè)務(wù)主鍵的再一個原因是,對于銀行系統(tǒng)而言安全性比性能更加重要,這時候就會考慮使用業(yè)務(wù)主鍵,既可以作為主鍵也可以作為冗余數(shù)據(jù),避免因?yàn)槭褂眠壿嬛麈I帶來的關(guān)聯(lián)丟失問題。如果由于某種原因?qū)е轮鞅砗妥颖黻P(guān)聯(lián)關(guān)系丟失的話,銀行可是會面臨無法挽回的損失的。為了杜絕這種情況的發(fā)生,業(yè)務(wù)主鍵需要在重要的表中有冗余存在,這種情況最好的處理方式就是直接使用業(yè)務(wù)主鍵了。例如***號、存折號、卡號等。所以通常銀行系統(tǒng)都要求使用業(yè)務(wù)主鍵,這個需求并不是出于性能的考慮而是出于安全性的考慮。
    ——SwitchBlade的總結(jié)

所以說明邏輯主鍵和業(yè)務(wù)主鍵的選擇并不是拍腦瓜的結(jié)果,而是根據(jù)不同的應(yīng)用場景、不同的需求決策的結(jié)果。

如果我們使用整數(shù)類型的自增id作為主鍵又會面臨什么問題呢?
對于數(shù)據(jù)量非常大的表后期往往會涉及到水平分表的需求,這時這個自增主鍵會成為阻礙。(其實(shí)關(guān)于這種情況也會有解決方案,請參見文章《又拍網(wǎng)架構(gòu)中的分庫設(shè)計(jì)》

ID數(shù)據(jù)類型的選擇

我們再換一個角度考慮主鍵的選擇:數(shù)據(jù)類型。

  • 整數(shù)類型
    整數(shù)類型往往是id列最好的選擇,因?yàn)樾首罡卟⑶铱梢允褂脭?shù)據(jù)庫的自增主鍵。

  • 字符串類型
    字符串類型相比整數(shù)類型肯定更消耗空間,也會比整數(shù)類型操作慢。我主要使用的是Mysql,關(guān)于這個話題的解釋建議看《高性能MySQL》第三版 P125。

我采用的方案(MySQL):使用自增id作為主鍵,以此來應(yīng)對插入效率問題;采用uuid做邏輯id,擁有了邏輯主鍵的諸多好處,而且可以用來應(yīng)對之后的水平分表。

當(dāng)前題目:數(shù)據(jù)庫的唯一標(biāo)示符(ID)的選擇
本文URL:http://muchs.cn/article36/picjsg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、網(wǎng)站建設(shè)外貿(mào)網(wǎng)站建設(shè)、ChatGPT、微信公眾號

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計(jì)