那么,我們在設(shè)計數(shù)據(jù)庫結(jié)構(gòu)時,到底該用什么來做主鍵呢?
對象數(shù)據(jù)庫說,主鍵只能是對象標識!
至于對象屬性是否唯一,那是由業(yè)務(wù)邏輯所決定的。如果業(yè)務(wù)邏輯規(guī)定訂單號不能重復(fù),就為訂單號建一個唯一索引好了,但它不是主鍵。
所以,當我們看到有些數(shù)據(jù)庫設(shè)計采用了額外的一個字段來專門充當主鍵,并用這個主鍵與其他表關(guān)聯(lián)的話,那就是已經(jīng)走到對象數(shù)據(jù)庫的門口了。什么“內(nèi)部碼”,“流水號”,“序列號”,“自增數(shù)”...通通都可以算是對象標識。
為什么SQL Server要提供自增量字段以及GUID的標識字段呢?都是為了這專門的主鍵字段服務(wù)的。
如果我們打算向?qū)ο髷?shù)據(jù)庫路上走得話,就請使用對象標識來做專門的主鍵字段吧。
當然,怎樣產(chǎn)生唯一的對象標識來做主鍵,這也是有說道的。
1.用自增量字段
自增量字段每次都會按順序遞增,可以保證在一個表里的主鍵不重復(fù)。除非超出了自增字段類型的最大值并從頭遞增,但這幾乎不可能。使用自增量字段來做主鍵是非常簡單的,一般只需在建表時聲明自增屬性即可。
自增量字段的長度可以很短,比如使用一個int類型就基本上夠用了。簡短的主鍵可以在大量數(shù)據(jù)和復(fù)雜的關(guān)系查詢中表現(xiàn)出更好的性能。
自增量的值都是需要在系統(tǒng)中維護一個全局的數(shù)據(jù)值,每次插入數(shù)據(jù)時即對此次值進行增量取值。當在當量產(chǎn)生唯一標識的并發(fā)環(huán)境中,每次的增量取值都必須最此全局值加鎖解鎖以保證增量的唯一性。這可能是一個并發(fā)的瓶頸,會牽扯一些性能問題。
還有,如果要搞分布式數(shù)據(jù)庫的話,這自增量字段就有問題了。因為,在分布式數(shù)據(jù)庫中,不同數(shù)據(jù)庫的同名的表可能需要進行同步復(fù)制。一個數(shù)據(jù)庫表的自增量值,就很可能與另一數(shù)據(jù)庫相同表的自增量值重復(fù)了。當然,這可以通過指定不同的遞增起始值來錯開,但總覺得不爽啊。
SQL Server中還可以使用timestamp類型的數(shù)據(jù)來做對象標識符,這可以使對象標識對整個數(shù)據(jù)庫都是唯一的,而不僅僅是對表唯一。但其優(yōu)缺點與表的自增字段一樣。
2.隨機數(shù)字段
隨機生成對象標識的方法實際就是碰運氣。按照某種復(fù)雜的隨機算法迅速產(chǎn)生對象標識,碰一碰對象標識不重復(fù)的運氣。只要這種算法產(chǎn)生的對象標識一萬年才可能重復(fù)一次,那你就可以在實際開發(fā)中應(yīng)用這種算法。比如,SQL Server中提供了uniqueidentifier類型,配合NEWID()函數(shù)來產(chǎn)生GUID,基本上可以應(yīng)用于所有主鍵需求了。
隨機生成標識不需要在系統(tǒng)中維護全局量,不存在自增字段那種加鎖解鎖的性能開銷,對于大量的并發(fā)處理來說是個福音。
同時,即使在分布式數(shù)據(jù)庫應(yīng)用中,不同數(shù)據(jù)庫產(chǎn)生的隨機值也是不同的。因此,在數(shù)據(jù)庫的同步復(fù)制中,標識相同的就一定是同一條記錄,就不會存在產(chǎn)生主鍵沖突的問題了。
不過,為了保證隨機的唯一性,需要大范圍的數(shù)值空間。這也使得主鍵字段比較大,在關(guān)聯(lián)查詢的時候有一定的性能損失,判斷GUID值是否相同總比判斷int值是否相同要多費些功夫。
在實際應(yīng)用中到底該用什么方案來產(chǎn)生對象標識需要根具體情況決定,以上方案僅僅是理論探討。
接下來要注意的就是主鍵的索引結(jié)構(gòu)的優(yōu)化了。
主鍵都要整成聚集索引。聚集索引在SQL Server內(nèi)部就是聚集表,聚集表是B樹結(jié)構(gòu),索引值存在B樹的中間節(jié)點中,而數(shù)據(jù)行就存放在B樹的頁節(jié)點上。也就是說,聚集索引和數(shù)據(jù)表其實是一個統(tǒng)一的整體結(jié)構(gòu)。因此,聚集索引查找和定位數(shù)據(jù)的效率要比一般索引高出很多。
此外,專門主鍵字段值是永遠都不變。聚集索引建值不變,聚集表的節(jié)點也不會調(diào)整,硬盤上的數(shù)據(jù)記錄塊基本不動窩的。這可以極大減少數(shù)據(jù)庫碎片,除非刪除數(shù)據(jù)行。數(shù)據(jù)庫的碎片少了,查詢數(shù)據(jù)自然要快些。
一般來說,我們存入數(shù)據(jù)庫中的數(shù)據(jù)總是有時間順序的,我們?nèi)粘2樵兒褪褂玫臄?shù)據(jù)也總是近期的數(shù)據(jù)。有的常用查詢甚至總是按時間順序倒排,比如,郵件列表,論壇帖子。如果主鍵采用的是自增字段,我們不妨將增量值設(shè)為-1或干脆搞成倒序的主鍵。這樣,查詢的排序與掃描索引的順序相同,畢竟硬盤的磁頭總喜歡從前往后讀,這會稍微提高一點讀取聚集索引的速度。
同樣,如果使用隨機主鍵值的方案,我們也建議采用與時間相關(guān)的隨機數(shù)值,而不是GUID。與時間相關(guān)的隨機數(shù)就是,雖然主鍵是隨機產(chǎn)生的,但后產(chǎn)生的隨機數(shù)應(yīng)該大于先產(chǎn)生的數(shù)。
為什么不用GUID呢?因為它生成的值就像頑皮的猴子,到處亂跳。于是,在每次插入記錄時,聚集索引節(jié)點中相鄰的值并不具有時間上的順序。而當我們習慣性地查詢近期數(shù)據(jù)時,硬盤的磁頭也需要像猴子般亂跳一通之后才能讀到按時間順序的所有數(shù)據(jù)。
如果采用有時間順序的隨機值,聚集索引插入數(shù)據(jù)時總往一個方向增加數(shù)據(jù)行的頁節(jié)點,與同一時期的數(shù)據(jù)行幾乎總相鄰。查詢同期數(shù)據(jù)時,磁頭就很少亂跳了。磁頭一次可以讀取一批數(shù)據(jù),效率有將有所提升。如果您用電子顯微鏡去觀察硬盤的表面,聚集索引的那顆B樹將生長得很正很齊。
對磁頭讀寫的優(yōu)化,是完美主義的程序員所追求的,是否采用完全看自己的心態(tài)?傊,高興就好。當然,等將來的大容量存儲設(shè)備都用上固態(tài)盤了,沒磁頭了,全電信號了,這些優(yōu)化就都沒有什么意義了。
所以呢,追求完美也是很痛苦的。吃了程序員這碗飯,就只能被IT的洪流卷著往前走。我們不過是這洪流中的一粒沙子,不知道又會被帶到何方?
王菲在一首歌中這樣唱到:一路上有人太早看透生命的線條命運的玄妙,有人太晚覺悟冥冥中該來則來無處可逃...
本文鏈接:http://www.95time.cn/tech/program/2008/5837.asp
出處:軟件真諦
責任編輯:bluehearts
上一頁 主鍵的故事 [1] 下一頁
◎進入論壇網(wǎng)絡(luò)編程版塊參加討論
|