本文出處:http://www.cnblogs.com/wy123/p/7581380.html
(保留出處並非什么原創作品權利,本人拙作還遠遠達不到,僅僅是為了鏈接到原文,因為后續對可能存在的一些錯誤進行修正或補充,無他)
關於自增列
自增列作為數據庫的一個特性之一,在MSSQL和MySQL以及Oracle中都被支持。
之前在網上發現一個類似的問題,是關於MySQL的:“為什么InnoDB表最好要有自增列做主鍵?”
自增列作為一項特性,(可能)會應用到表的設計方面,不管是在那種數據庫平台下。
拋開具體的數據庫平台,這個問題可以從更泛的層次去思考,為什么在設計表的時候,需要增加一個自增列做主鍵?
或者反過來問,設計表的時候,增加一個自增列做主鍵有什么好處?
多數時候,自增列作為一個冗余字段,在設計表的時候是非必須的,也就是說,一個表中,要不要自增列(甚至要不要自增列作為主鍵)都是可選的,不是說沒有自增列某些表就無法滿足業務需求的。
當設計表的時候增加一個自增列已經成為一個非強制的要求的時候,做過數據庫設計的人對此問題會習以為常。
但是突然有人問上一句“為什么”,還是很難一句話解釋清楚的,
比如剛工作不久的人問的問題就很尖銳:“為什么表中要加一個自增列?有什么好處?看起來這個自增列是可有可無的?”
這是一個DB Designer必須要考慮清楚且要隨時去回答一些人的質疑的問題。
業務主鍵與邏輯主鍵的選擇
這之前首先解釋兩個概念:業務主鍵與邏輯主鍵,以設計一個User表為例,有UserId,UserName,CreateDate等等一些屬性
業務主鍵:把具有實際含義的字段作為主鍵
通常情況下可以把UserId作為業務主鍵,主鍵就暗含了非空+唯一性,一個表中每個UserId是不能為空且唯一的,UserId作為主鍵並沒有什么不妥當的。
通常來說,業務主鍵在一個表中的地位很明顯,也很直觀,比如UserId,訂單ID,CustomerId,登錄名等等。
邏輯主鍵:采用與業務無關的唯一性的字段做主鍵,或稱之為“偽主鍵”
自增列或者GUID都可以實現所謂的邏輯主鍵,這里暫且不不討論GUID(自增與GUID也是一個很大的話題),在User表中增加一個自增列作為主鍵,因為自增列是也符合主鍵的特性,也即非空且唯一。
這個字段的第一個特點是與任何業務邏輯無關,第二個特點是對業務來說是不可見的,
比如設計一張用戶表,在滿足存儲所有用戶信息的條件下,該字段作為一個獨立的列存在,而不描述任何業務含義。
以上就是所謂的業務主鍵和邏輯主鍵,實話說,僅僅從表面上看,后者並沒有什么明顯的優勢,甚至可以說,如果僅僅就這一張表來說的話,有點冗余的感覺。
但是實際應用中,一個應用是具體一系列復雜的表來支撐的,表與表之間存在着一系列的關聯關系,比如1對1或者1對多的關系,此時,自增列的作用就可以提現出來了。
業務主鍵存在的問題
首先說業務主鍵存在的弊端,業務主鍵必然要與業務邏輯掛鈎,這就意味着業務主鍵可能是一個或者多個字段的結合,甚至對於每一個業務主鍵字段都有要求,
比如訂單號要求以DH0000000000X開頭,或者通常情況下的UserId,比如博客園的登錄Id,是介於一定長度之間的字符與數字的組合
這樣存在一下幾個弊端:
1,當前表的主鍵與其他表的存在關聯關系的時候,必然要存儲當前這個表的業務主鍵信息,甚至不止一個字段,這樣無疑增加了表與表之間關聯的復雜性與存儲空間。
同理,在表之間join的時候,必然要用基於業務主鍵的各個條件連接,業務增加SQL的復雜性和降低SQL的執行效率。
2,既然是業務主鍵,也存在潛在的修改的可能性,有人說主鍵還會修改,不可能吧,其實這種情況還真不少,比如呢?
博客園的登錄名,作為一個類似於UserId的東西,可以認為是一個業務主鍵,其特點就是非空+唯一的
但是這個用戶名就是可以修改的,如果其他跟用戶信息相關的表存儲了用戶名,用戶名有修改之后,要達到數據的一致性,要修改一系列存儲了用戶名相關的表。
再比如,如果用郵箱作為用戶名進行注冊,比如雅虎郵箱關閉之后,
注冊的各種寶寶賬號,如果相關表中直接存儲的這個業務主鍵,也即直接存儲的這個郵箱,在需要給用戶發送郵件的地方存一個郵箱信息字段,
那么修改個人郵箱的時候,要涉及多少張表的修改?
另外,在SQLServer中,在Update主鍵的時候,實際上執行行的是一個delete和insert操作,也就是說會先刪除要更新的那條記錄,再插入一條記錄。
這也是一個代價相對較大的操作。
3,在SQLServer中,默認情況下主鍵就是聚集索引,非聚集索引的葉子節點會存儲一個指向聚集索引的Key值的,
一旦一個大的設計了一個較大的主鍵,那么會導致任何非聚集索引都會包含一個較大的聚集索引key值,這也無疑使得非聚集索引變得膨脹和占用較大的存儲空間。
4,不得不考慮的索引碎片問題,同3,默認情況下主鍵就是聚集索引,如果聚集索引是一個無規律的字段,新填充進來的數據必然會根據排序規則隨機存放,
一旦隨機存放,極有可能導致不斷的頁拆分與伴隨着碎片的產生,這對性能來說也不是什么好現象
以上是沒有自增列的時候,業務字段作為主鍵可能潛在的問題。
自增列作為主鍵解決的問題
如果使用自增列作為主鍵,均可以避免上述問題,
對於問題1和2,表與表之間的關聯,一個自增列就可以完成了,在表之間的join關系的時候,從表僅需存儲主表的一個自增字段,就可以將表之間的關聯關系串起來
比如博客園的用戶名修改,當你修改了用戶名,因為其他跟用戶相關的表存儲的是一個與任何業務沒有關聯關系的自增字段,隨你怎么修改用戶名,其他表是根據自增Id來管理的,
自增列的信息永遠使用戶是不可見,也就意味着永遠不會被修改。
對於問題3和4,自增列做主鍵的時候是唯一的,這就意味着不會存在復合主鍵列的情況,同時也會消除因為隨機字段做聚集索引造成的碎片問題。
自增列主鍵與聚集索引的補充
另外一個不可回避的話題就是,聚集索引在查詢時候的寶貴性,聚集索引(B樹)本身就數據,可以通過聚集索引直接找到數據。
在進行某些查詢或者排序的時候,聚集索引的對於查詢性能的好處,一旦將聚集索引建立在一個無業務無關的字段上,聚集索引的價值就白白浪費了。
在SQLServer數據庫中,主鍵與聚集索引沒有必然的關系(MySQL的InnoDB就例外了),聚集索引與其所在字段的是否為空和唯一性也沒有必然的關系
意味着自增列僅僅是為了消除業務主鍵可能潛在的問題而存在,而不影響聚集索引的自由選擇。
如果設計了自增列,自增列做了主鍵,主鍵又是聚集索引,我們知道聚集索引的寶貴性,一張表只能有一個聚集索引。
在sqlserver中,這一點設計的比較靈活,就是主鍵不一定非要是聚集索引,可以指定主鍵是非聚集索引,指定其他鍵為聚集索引
同時,對於有唯一性含義的字段,可以指定為NOT NULL+UNIQUE約束,也保證其屬性的非空和唯一性。
對於聚集索引的選擇,可以根據具體的業務需要作出合理的選擇,比如經常需要按照時間范圍查詢或者排序,那么就可以將聚集索引建立在CreateDate字段上。
但是在MySQL的InnoDB中,就沒有sqlserver上這一點靈活,
主鍵必須是聚集索引(即便是沒有主鍵或者唯一約束字段,會自動生成一個主鍵做聚集索引),這一點沒有商量的余地,因此,在MySQL中,“為什么InnoDB表最好要有自增列做主鍵?”這一點就顯得更加有必要。
自增列存在的問題
以上說明了,自增列作為主鍵解決了業務字段作為主鍵存在的問題,但是自增列也並非萬能的,
1,僅僅從存儲的角度來看,一個最直觀的特點就是冗余存儲,也就存儲了一個與業務無關的字段,盡管這個字段有其本身存在的諸多的合理性。
2,並發插入的時候,如果自增列作為聚集索引,可能導致新進來的數據存儲在一個邏輯頁面上,也就是所謂熱點頁。
對於這一點,嚴格來說,也不僅僅是自增列引起的,而是聚集索引的選擇與並發寫入時候本身就是一個矛盾體。
我們總是想讓數據按照某種方式來順序存儲,比如按照時間順序存儲,但是在並發存儲的時候又會發生熱點頁的爭用問題。
3,對於分布式數據庫,或者多個數據庫的數據進行合並作為OLAP分析的時候,每個庫中的自增列是獨立的,可能會存在沖突的問題
不過這個問題也不是問題,自增列可以指定自增的開始值和遞增規律,來繞開不同數據源中自增列沖突的問題。
比如A,B兩個業務數據庫的同樣性質的一個表,可以指定在A庫中,表上的自增列從1開始,B庫中的自增列從100000000開始
甚至可以指定自增每次增加的值,A庫的表的自增列從1開始,每次加2,結果就是1,3,5,7,9,11……;B庫的表的自增列從2開始,每次加2,結果就是2,4,6,8,10……
設置可以根據需求,對自增列的自增特點做的更復雜,這樣就會避免在對多個庫的數據進行合並分析的時候自增列了的沖突。
4,類似於自增列的Sequence,Sequence與自增列有部分類似的地方,可以看做是自增列在功能上的擴展。關於Sequence就不多闡述了。
自增列使用的范例:
最后看一下參考一下微軟給出的AdventureWorks2012示例數據庫中的訂單表的設計,有興趣的可以看一下表結構的字段信息。
從業務上看,訂單信息,在業務上看,必然有一個能決定訂單唯一性的因素:比如CustomerId+OrderDate從理論上說就可以決定唯一的一條數據,
但是他依然使用了一個自增列作為主鍵,並且在子表,也即訂單明細表中使用主表的自增列作為外鍵,
而對於訂單明細表,依然有一個自增列作為主鍵,因此,可以認為自增列作為邏輯主鍵,相對還是有一定的優勢的。
總結:
自增列作為一個冗余存儲的字段,在復雜的表結構之中,起着承上啟下的關聯作用,相當於使用一種輕量級的冗余,來簡化業務主鍵帶來的繁復。
作為一個與任何業務無關的字段,避免業務主鍵在某些特殊情況下修改帶來的一系列問題。