背景:數年的工做中,已經設計了不少系統或產品的數據庫,有單機的、有局域網環境下的、也有互聯網環境下的,對於不一樣的環境,設計考慮都有所不一樣。即便對於相同的環境,也會由於業務或者數據量的不一樣而有不一樣的設計。近期,又要設計一款互聯網產品的數據庫(MySQL服務)。通過以前的積累,在表的ID設計這個環節就進行了大量的分析、比較、學習,對ID的設計也有了更系統和深入的認知,把本身學習實踐到的知識總結下來,分享給你們。html
對於關係數據庫來講,設計每一個表的第一步都會肯定其主鍵,主鍵就是ID。在「常識」之中,int型的自增id,字符串類型的uuid,其餘與業務相關的惟一鍵…都是咱們做爲主鍵的選擇。那麼是否是說在一張表中只要能保證值惟一的屬性列均可以作爲主鍵或者更合適作主鍵呢?sql
那咱們首先清晰幾個概念:數據庫
邏輯主鍵(代理主鍵):在數據庫表中採用一個與當前表中業務邏輯信息無關的字段做爲其主鍵,或稱爲「僞主鍵」;安全
業務主鍵(天然主鍵):在數據庫表中把具備業務邏輯含義的字段做爲主鍵;架構
舉一個很常見的例子:一張用戶信息表,列屬性有id、用戶名、手機號…,其中用戶名和手機號(做爲登陸帳號兩者都是惟一的)。其中id即可做爲邏輯主鍵,用戶名和手機號均可以做爲業務主鍵。那我是否是能夠隨便選一個,甚至我選擇了業務主鍵均可以不要邏輯主鍵?ide
那麼咱們首先來看看邏輯主鍵和業務主鍵之間紛紛烈烈的觀點分歧:post
支持邏輯主鍵性能
表經過主鍵來保證每條記錄的惟一性,表的主鍵應當不具備任何業務含義,由於任何有業務含義的列都有改變的可能性。關係數據庫學的最重要的一個理論就是:不要給關鍵字賦予任何業務意義。假如關鍵字具備了業務意義,當用戶決定改變業務含義,也許他們想要爲關鍵字增長几位數字或把數字改成字母,那麼就必須修改相關的關鍵字。一個表中的主關鍵字有可能被其餘表做爲外鍵。就算是一個簡單的改變,譬如在客戶號碼上增長一位數字,也可能會形成極大的維護上的開銷。學習
爲了使表的主鍵不具備任何業務含義,一種解決方法是使用代理主鍵,例如爲表定義一個不具備任何業務含義的ID字段(也能夠叫其餘的名字),專門做爲表的主鍵。
——孫衛琴《精通Hibernate:Java對象持久化技術詳解》P8ui
使用邏輯主鍵的主要緣由是,業務主鍵一旦改變則系統中關聯該主鍵的部分的修改將會是不可避免的,而且引用越多改動越大。而使用邏輯主鍵則只須要修改相應的業務主鍵相關的業務邏輯便可,減小了由於業務主鍵相關改變對系統的影響範圍。業務邏輯的改變是不可避免的,由於「永遠不變的是變化」,沒有任何一個公司是一成不變的,沒有任何一個業務是永遠不變的。最典型的例子就是***升位和駕駛執照號換用***號的業務變動。並且現實中也確實出現了***號碼重複的狀況,這樣若是用***號碼做爲主鍵也帶來了難以處理的狀況。固然應對改變,能夠有不少解決方案,方案之一是作一新系統與時俱進,這對軟件公司來講確實是件好事。使用邏輯主鍵的另一個緣由是,業務主鍵過大,不利於傳輸、處理和存儲。我認爲通常若是業務主鍵超過8字節就應該考慮使用邏輯主鍵了,由於int是4字節的,bigint是8字節的,而業務主鍵通常是字符串,一樣是 8 字節的 bigint 和 8 字節的字符串在傳輸和處理上天然是 bigint 效率更高一些。想象一下 id 爲」12345678」 和 id爲12345678 的彙編碼的不一樣就知道了。固然邏輯主鍵不必定是 int 或者 bigint ,而業務主鍵也不必定是字符串也能夠是 int 或 datetime 等類型,同時傳輸的也不必定就是主鍵,這個就要具體分析了,可是原理相似,這裏只是討論一般狀況。同時若是其餘表須要引用該主鍵的話,也須要存儲該主鍵,那麼這個存儲空間的開銷也是不同的。並且這些表的這個引用字段一般就是外鍵,或者一般也會建索引方便查找,這樣也會形成存儲空間的開銷的不一樣,這也是須要具體分析的。
使用邏輯主鍵的再一個緣由是,使用 int 或者 bigint 做爲外鍵進行聯接查詢,性能會比以字符串做爲外鍵進行聯接查詢快。原理和上面的相似,這裏再也不重複。
使用邏輯主鍵的再一個緣由是,存在用戶或維護人員誤錄入數據到業務主鍵中的問題。例如錯把 RMB 錄入爲 RXB ,相關的引用都是引用了錯誤的數據,一旦須要修改則很是麻煩。若是使用邏輯主鍵則問題很好解決,若是使用業務主鍵則會影響到其餘表的外鍵數據,固然也能夠經過級聯更新方式解決,可是不是全部都能級聯得了的。
——SwitchBlade的總結
支持業務主鍵
若是你的表中包含一列能確保惟1、非空以及可以用來定位一條記錄,就別僅僅由於傳統而以爲有必要再加上一個僞主鍵。
——Bill Karwin 《SQL反模式》 p41
使用業務主鍵的主要緣由是,增長邏輯主鍵就是增長了一個業務無關的字段,而用戶一般都是對於業務相關的字段進行查找(好比員工的工號,書本的 ISBN No. ),這樣咱們除了爲邏輯主鍵加索引,還必須爲這些業務字段加索引,這樣數據庫的性能就會降低,並且也增長了存儲空間的開銷。因此對於業務上確實不常改變的基礎數據而言,使用業務主鍵不失是一個比較好的選擇。另外一方面,對於基礎數據而言,通常的增、刪、改都比較少,因此這部分的開銷也不會太多,而若是這時候對於業務邏輯的改變有擔心的話,也是能夠考慮使用邏輯主鍵的,這就須要具體問題具體分析了。使用業務主鍵的另一個緣由是,對於用戶操做而言,都是經過業務字段進行的,因此在這些狀況下,若是使用邏輯主鍵的話,必需要多作一次映射轉換的動做。我認爲這種擔憂是多餘的,直接使用業務主鍵查詢就能獲得結果,根本不用管邏輯主鍵,除非業務主鍵自己就不惟一。另外,若是在設計的時候就考慮使用邏輯主鍵的話,編碼的時候也是會以主鍵爲主進行處理的,在系統內部傳輸、處理和存儲都是相同的主鍵,不存在轉換問題。除非現有系統是使用業務主鍵,要把現有系統改爲使用邏輯主鍵,這種狀況纔會存在轉換問題。暫時沒有想到還有什麼場景是存在這樣的轉換的。
使用業務主鍵的再一個緣由是,對於銀行系統而言安全性比性能更加劇要,這時候就會考慮使用業務主鍵,既能夠做爲主鍵也能夠做爲冗餘數據,避免由於使用邏輯主鍵帶來的關聯丟失問題。若是因爲某種緣由致使主表和子表關聯關係丟失的話,銀行但是會面臨沒法挽回的損失的。爲了杜絕這種狀況的發生,業務主鍵須要在重要的表中有冗餘存在,這種狀況最好的處理方式就是直接使用業務主鍵了。例如***號、存摺號、卡號等。因此一般銀行系統都要求使用業務主鍵,這個需求並非出於性能的考慮而是出於安全性的考慮。
——SwitchBlade的總結
因此說明邏輯主鍵和業務主鍵的選擇並非拍腦瓜的結果,而是根據不一樣的應用場景、不一樣的需求決策的結果。
若是咱們使用整數類型的自增id做爲主鍵又會面臨什麼問題呢?
對於數據量很是大的表後期每每會涉及到水平分表的需求,這時這個自增主鍵會成爲阻礙。(其實關於這種狀況也會有解決方案,請參見文章《又拍網架構中的分庫設計》
咱們再換一個角度考慮主鍵的選擇:數據類型。
整數類型:
整數類型每每是id列最好的選擇,由於效率最高而且可使用數據庫的自增主鍵。
字符串類型
字符串類型相比整數類型確定更消耗空間,也會比整數類型操做慢。我主要使用的是Mysql,關於這個話題的解釋建議看《高性能MySQL》第三版 P125。
我採用的方案(MySQL):使用自增id做爲主鍵,以此來應對插入效率問題;採用uuid作邏輯id,擁有了邏輯主鍵的諸多好處,並且能夠用來應對以後的水平分表。