天天看點

資料庫模型設計——主鍵的設計

在資料庫設計時,主要就是對實體和關系的設計,實體表現出來就是表,關系表現出來就是外鍵。而對于一個表,由兩部分組成:主鍵和屬性。主鍵的簡單定義就是表中為每一行資料的唯一辨別。其實更準确的說法,每一行資料的唯一辨別是候選鍵(candidate key),一個表中可以有很多個候選鍵,主鍵是候選鍵中的一個,主要用于更友善的檢索和管理資料。一個表中可以有多個候選鍵,但是隻有一個主鍵。由于主鍵常常用于檢索資料,也用于表之間的關聯,是以主鍵的設計的好壞将會嚴重影響資料操作的性能。下面來介紹下主鍵設計的幾個考慮因素。

最常見的主鍵資料類型是數字類型、固定長度的字元類型和guid類型。通常情況下,rdbms會在主鍵上建立聚集索引(sql server預設都這麼做),由于我們使用b-tree的資料結構來存儲索引資料,是以一般對主鍵有以下兩個要求:

越短越好——越短在一個page中存儲的節點越多,檢索速度就越快。

順序增長——如果每一條插入的資料的主鍵都比前面的主鍵大,那麼b-tree上的節點也是順序增長的,不會造成頻繁的b-tree分割。

越短越好是為了查詢的速度快,順序增長是為了插入速度快。

有了這兩個要求,我們再來分析下各個資料類型:

數字類型:根據資料量決定是用int16還是int32或者int64,能用int32的就不需要使用int64。

字元類型:基本不滿足前面提到的2點要求,字元類型一般不會很短,而且也很可能不是順序增長的,是以不是特别推薦的主鍵類型。當然如果确實業務需求使用字元類型,那麼也盡量使用char(xx)而不要使用varchar(xx),因為在rdbms中,對于定長字元串和變成字元串的資料結構和處理是不一樣的,varchar的性能更差。

guid類型:這個類型并不是所有資料庫都有對應的資料類型,sql server有uniqueidentifier,mysql沒有。guid類型在sql server中是16個位元組,不算短,比4個位元組的int32長多了。在插入新資料時,guid一般都是使用newid()這樣的生成随機guid的方式生成的,是以也不是順序增長的,在插入速度上不會很快。

通過上面的比較,我們知道使用數字類型是更好的方式,那麼我們為什麼還會有人使用guid和字元串來當主鍵呢?那是因為:

相對于數字類型,字元類型更易讀易記,在檢索關聯的資料時,更友善直接。

guid的優勢是全球唯一,也就是說同樣的系統,如果部署了多套環境,那麼裡面的資料的主鍵仍然是唯一的,這樣有助于資料的內建。典型的例子就是一個系統在全國每個省份都部署一套,每個省份的資料各種錄入,互不幹擾,然後再把每個省的資料內建起來為總部做分析。

前面說到一個表可能有很多個唯一辨別的候選鍵,那麼這麼多候選鍵中,哪個應該拿來做主鍵呢?一種方案是再建立一個獨立的字段作為主鍵,該字段并沒有業務含義,隻是一個自增列或者流水号,用于唯一辨別每一行資料,這是資料庫主鍵。另外一種方案是選擇其中較短較常用的屬性作為主鍵,這是業務主鍵。個人建議是不要使用任何有業務含義的字段作主鍵,而是使用一個自增的(或者系統生成的)沒有實際業務意義的字段作為主鍵。為什麼呢?主要是出于以下考慮:

具有業務意義的字段很可能是使用者從系統錄入的,不要信任使用者的任何輸入,隻要是使用者自己錄入的,那麼就很有可能錄錯了,如果發現錄入錯誤,這個時候再對主鍵進行修改,将會涉及到大量關聯的外鍵表的修改,是很麻煩的一件事情。比如在做人員表的時候,就不要使用員工号或者身份證号做主鍵。

具有業務意義的字段雖然在目前階段是唯一的,是不變的,但是并不能保證随着公司政策變動、業務調整等原因,導緻該業務字段需要修改,以滿足新的業務要求,這個時候要修改主鍵也是很麻煩的事情。比如部門表,我們以部門code作為主鍵,但是後來部門變動,code修改,則系統部門表的主鍵也得更改。

還有一個原因是業務主鍵在資料錄入的時候不一定是明确知道的,有時我們會在不知道業務主鍵的情況下,就錄入其他相關資訊,這個時候,如果使用業務主鍵做資料庫的主鍵,那麼資料将無法錄入。比如員工表把員工号作為主鍵,那麼員工還沒有入職,沒有員工号的時候,hr需要先維護一些該預入職員工的資訊是不可能的。

聯合主鍵就是以多個字段來唯一辨別每一行資料。前面已經說到主鍵應該越短越好,而且是建議是一個沒有意義的自增列,那麼是不是就不會再需要聯合主鍵呢?答案是否定的,我們仍然可能會使用到聯合主鍵。聯合主鍵主要使用在多對多的關系時,中間表就需要使用聯合主鍵。在簡單的多對多關系中,我們不需要為中間的關聯建立實體,是以中間表可能就隻需要兩列,分别是兩個實體表的主鍵。

主鍵值的生成可以參考nhibernate的配置,概況下來主要有這麼幾種生成方式:

自增,這是sql server常用的主鍵生成方式,完全由資料庫管理主鍵的值。

sequence對象,這是oracle常用的主鍵生成方式,現在sql server已支援。主要是在資料庫中有一個sequence對象,通過該對象生成主鍵。

guid,這是用于guid類型的主鍵,可以使用newid()這種資料庫提供的函數,或者使用程式生成guid并指派。

hilo值,這是一種使用高低位算法生成的數字值的主鍵。該值由nhibernate程式内部生成。

其他程式指派,完全由程式根據自己的算法生成并指派。

在概念和作用上,主鍵與索引是完全兩個不同的東西,但是由于我們大部分情況下都是使用主鍵檢索資料,是以大部分資料庫的預設實作,在建立主鍵時會自動建立對應的索引。

以sql server為例,預設情況下,建立主鍵的列,就會建立聚集索引,但是實際上,我們可以在建立主鍵時不使用聚集索引。另外還有一個唯一限制(索引)的概念,該索引中的資料必須是唯一不能重複的,感覺和主鍵的意義一樣,但是還是有一點點差別。

主鍵是隻能由一個,而唯一限制(索引)在一個表中可以有多個。

主鍵不能為空,而唯一限制(索引)是可以為空的。