|
1969年8月8日,在北京協(xié)和醫(yī)院降生了一個(gè)漂亮的小女孩。接生的阿姨說,她的聲音這么大,好象想要全世界的人都聽到。
后來,她的父親為她取了一個(gè)很好聽的名字,叫“王菲”。于是,所有的小朋友就叫她“王菲”,“王菲”就是她童年的主鍵。
在她上初二的時(shí)候,認(rèn)識(shí)了二班另一個(gè)叫“王菲”的同學(xué),而且和她同一天生日。不過,同學(xué)們常常將她倆弄錯(cuò),后來,就分別叫她們“大王菲”和“小王菲”。“大王菲”就是她在同學(xué)們心目中的主鍵。
在大王菲18歲的那一年,她領(lǐng)到了她的身份證。從此,她有了新的身份標(biāo)識(shí)“100321690808022”,這一標(biāo)識(shí)可以唯一區(qū)別中國大陸的每一個(gè)人。同時(shí),原來二班的“小王菲”也領(lǐng)到了她自己的身份證,“100321690808006”。于是,人們就可以用身份證號(hào),唯一標(biāo)識(shí)兩個(gè)“王菲”了。身份證號(hào)就是她成年后的主鍵。
由于她的歌唱的非常好,沒多久就成了歌星。后來她去了香港發(fā)展,娛樂公司非要將她的名字更改為“王靖雯”,雖然她并不喜歡這個(gè)名字,但為了事業(yè)的發(fā)展還是同意了。“王靖雯”就成了她在娛樂圈里的主鍵。
“王靖雯”的歌打動(dòng)了許許多多的歌迷,很快就成了歌壇天后。歌迷們將“王靖雯”這一主鍵與無數(shù)條動(dòng)聽的歌曲記錄關(guān)聯(lián)在了一起,并形成了一個(gè)龐大的歌迷數(shù)據(jù)庫。
沒多久,王靖雯和一個(gè)彈電吉他的小子相愛了。那小子說,還是“王菲”這個(gè)名字好聽,于是,“王靖雯”又變回“王菲”了。主鍵被那小子修改了,這下麻煩大了。歌迷們都糊涂了,是將她的歌關(guān)聯(lián)到“王菲”還是“王靖雯”呢?這一主鍵的修改,導(dǎo)致歌迷數(shù)據(jù)庫的一次大混亂。
再后來呢,她和那個(gè)彈電吉他的小子結(jié)婚了,香港政府將他們的身份證號(hào)碼,用結(jié)婚證書關(guān)聯(lián)起來。這本也算一樁好事,可是,月老在酒醒之后發(fā)現(xiàn)了這一錯(cuò)誤,就將關(guān)聯(lián)的記錄刪除了。陰差陽錯(cuò),命運(yùn)使然,她和那個(gè)彈電吉他的小子只好分手了。
但是,正如接生的阿姨說的那樣,她的聲音的確讓全世界的人都聽到了!
講完這個(gè)故事之后,我們是否能領(lǐng)悟到數(shù)據(jù)庫設(shè)計(jì)的一些哲理呢?
什么是主鍵?
關(guān)系數(shù)據(jù)庫說,為了唯一區(qū)分表的每一行記錄,必須為表確定一個(gè)主鍵。主鍵可以是一個(gè)或多個(gè)列組成,這些主鍵列的值不能重復(fù)。主鍵是兩個(gè)表進(jìn)行關(guān)聯(lián)的基礎(chǔ),所謂“關(guān)系”體現(xiàn)的是一個(gè)表的字段與另一個(gè)表的主鍵的關(guān)聯(lián)。
在日常的應(yīng)用項(xiàng)目開發(fā)中,我們常常都會(huì)面臨“該拿什么字段來做主鍵”的難題。
用名字做主鍵?一班的“王菲”和二班的“王菲”可能重復(fù)。
用身份證號(hào)做主鍵?在大陸也許不會(huì)重復(fù),去香港后,人家卻不認(rèn)。本不相信第二代身份證號(hào)會(huì)重復(fù),但俺的一位同事正好撞上!
同樣,主鍵值的更新也是麻煩事兒。“王靖雯”變成“王菲”那段日子,貨架上一張唱片是王靖雯的,另一張又是王菲的,許多歌迷還以為又有新人出現(xiàn)了。
請(qǐng)不要說可以級(jí)聯(lián)更新,將你那個(gè)做主鍵的字段加長(zhǎng)兩位試試?想當(dāng)年,當(dāng)身份證號(hào)從15位增加到18位時(shí),給銀行、證券、政府、企業(yè)...等多條戰(zhàn)線上的兄弟們制造了怎樣的痛苦啊!
為什么會(huì)出現(xiàn)這些問題呢?
原因就是:錯(cuò)誤地把對(duì)象屬性當(dāng)作對(duì)象標(biāo)識(shí)!
對(duì)象屬性和對(duì)象標(biāo)識(shí)都是對(duì)象數(shù)據(jù)庫中的概念。
什么是對(duì)象屬性呢?
就是業(yè)務(wù)邏輯上涉及的任何可變信息,什么“姓名”、“性別”、“身份證號(hào)”、“訂單號(hào)”...通通都是對(duì)象屬性。對(duì)象屬性總會(huì)變化的,只是有些變得快,有些變得慢而已。
對(duì)象標(biāo)識(shí)是啥?就是唯一區(qū)分?jǐn)?shù)據(jù)對(duì)象的鑒別符,對(duì)象標(biāo)識(shí)存在的唯一目的就是區(qū)分對(duì)象,除此之外沒有任何業(yè)務(wù)邏輯上的意義。
不管王菲的屬性值怎樣變化,但王菲還是王菲,不是二班的那個(gè)“王菲”。也就是說,王菲的靈魂未變,她是不會(huì)改變的,就象哲學(xué)上所說的“不以人的意志為轉(zhuǎn)移”。這種唯一表示對(duì)象本身的東西,就是對(duì)象標(biāo)識(shí)!
對(duì)象標(biāo)識(shí)是唯一的。也就是說,即使兩個(gè)對(duì)象,他們的屬性完全一樣,但它們的對(duì)象標(biāo)識(shí)是不同的。畢竟,同名同姓甚至同一天出生的大王菲和小王菲是兩個(gè)不同的人。
對(duì)象的標(biāo)識(shí)是永恒不變的。一旦對(duì)象產(chǎn)生,它的標(biāo)識(shí)就自然地、唯一地產(chǎn)生了。盡管王菲換了名,身份證號(hào)也變過,但王菲的對(duì)象標(biāo)識(shí)未變。即使到了下個(gè)世紀(jì),她的對(duì)象標(biāo)識(shí)也將依然存在于歌迷們的們的心中。
對(duì)象的標(biāo)識(shí)是描述關(guān)系的基礎(chǔ)。王菲唱的歌是王菲唱的,不是初二二班的那個(gè)“王菲”唱的。王靖雯唱的歌就是王菲唱的歌,有的歌迷只將歌曲和歌手的人名關(guān)聯(lián)起來,難怪會(huì)出混亂。香港政府也犯相同的錯(cuò)誤,將王菲的身份證號(hào)碼這一內(nèi)部屬性,跟那個(gè)彈電吉他的小子關(guān)聯(lián)起來,也許就是命運(yùn)的錯(cuò)誤。
那么,我們?cè)谠O(shè)計(jì)數(shù)據(jù)庫結(jié)構(gòu)時(shí),到底該用什么來做主鍵呢?
對(duì)象數(shù)據(jù)庫說,主鍵只能是對(duì)象標(biāo)識(shí)!
至于對(duì)象屬性是否唯一,那是由業(yè)務(wù)邏輯所決定的。如果業(yè)務(wù)邏輯規(guī)定訂單號(hào)不能重復(fù),就為訂單號(hào)建一個(gè)唯一索引好了,但它不是主鍵。
所以,當(dāng)我們看到有些數(shù)據(jù)庫設(shè)計(jì)采用了額外的一個(gè)字段來專門充當(dāng)主鍵,并用這個(gè)主鍵與其他表關(guān)聯(lián)的話,那就是已經(jīng)走到對(duì)象數(shù)據(jù)庫的門口了。什么“內(nèi)部碼”,“流水號(hào)”,“序列號(hào)”,“自增數(shù)”...通通都可以算是對(duì)象標(biāo)識(shí)。
為什么SQL Server要提供自增量字段以及GUID的標(biāo)識(shí)字段呢?都是為了這專門的主鍵字段服務(wù)的。
如果我們打算向?qū)ο髷?shù)據(jù)庫路上走得話,就請(qǐng)使用對(duì)象標(biāo)識(shí)來做專門的主鍵字段吧。
當(dāng)然,怎樣產(chǎn)生唯一的對(duì)象標(biāo)識(shí)來做主鍵,這也是有說道的。
1.用自增量字段
自增量字段每次都會(huì)按順序遞增,可以保證在一個(gè)表里的主鍵不重復(fù)。除非超出了自增字段類型的最大值并從頭遞增,但這幾乎不可能。使用自增量字段來做主鍵是非常簡(jiǎn)單的,一般只需在建表時(shí)聲明自增屬性即可。
自增量字段的長(zhǎng)度可以很短,比如使用一個(gè)int類型就基本上夠用了。簡(jiǎn)短的主鍵可以在大量數(shù)據(jù)和復(fù)雜的關(guān)系查詢中表現(xiàn)出更好的性能。
自增量的值都是需要在系統(tǒng)中維護(hù)一個(gè)全局的數(shù)據(jù)值,每次插入數(shù)據(jù)時(shí)即對(duì)此次值進(jìn)行增量取值。當(dāng)在當(dāng)量產(chǎn)生唯一標(biāo)識(shí)的并發(fā)環(huán)境中,每次的增量取值都必須最此全局值加鎖解鎖以保證增量的唯一性。這可能是一個(gè)并發(fā)的瓶頸,會(huì)牽扯一些性能問題。
還有,如果要搞分布式數(shù)據(jù)庫的話,這自增量字段就有問題了。因?yàn)椋诜植际綌?shù)據(jù)庫中,不同數(shù)據(jù)庫的同名的表可能需要進(jìn)行同步復(fù)制。一個(gè)數(shù)據(jù)庫表的自增量值,就很可能與另一數(shù)據(jù)庫相同表的自增量值重復(fù)了。當(dāng)然,這可以通過指定不同的遞增起始值來錯(cuò)開,但總覺得不爽啊。
SQL Server中還可以使用timestamp類型的數(shù)據(jù)來做對(duì)象標(biāo)識(shí)符,這可以使對(duì)象標(biāo)識(shí)對(duì)整個(gè)數(shù)據(jù)庫都是唯一的,而不僅僅是對(duì)表唯一。但其優(yōu)缺點(diǎn)與表的自增字段一樣。
2.隨機(jī)數(shù)字段
隨機(jī)生成對(duì)象標(biāo)識(shí)的方法實(shí)際就是碰運(yùn)氣。按照某種復(fù)雜的隨機(jī)算法迅速產(chǎn)生對(duì)象標(biāo)識(shí),碰一碰對(duì)象標(biāo)識(shí)不重復(fù)的運(yùn)氣。只要這種算法產(chǎn)生的對(duì)象標(biāo)識(shí)一萬年才可能重復(fù)一次,那你就可以在實(shí)際開發(fā)中應(yīng)用這種算法。比如,SQL Server中提供了uniqueidentifier類型,配合NEWID()函數(shù)來產(chǎn)生GUID,基本上可以應(yīng)用于所有主鍵需求了。
隨機(jī)生成標(biāo)識(shí)不需要在系統(tǒng)中維護(hù)全局量,不存在自增字段那種加鎖解鎖的性能開銷,對(duì)于大量的并發(fā)處理來說是個(gè)福音。
同時(shí),即使在分布式數(shù)據(jù)庫應(yīng)用中,不同數(shù)據(jù)庫產(chǎn)生的隨機(jī)值也是不同的。因此,在數(shù)據(jù)庫的同步復(fù)制中,標(biāo)識(shí)相同的就一定是同一條記錄,就不會(huì)存在產(chǎn)生主鍵沖突的問題了。
不過,為了保證隨機(jī)的唯一性,需要大范圍的數(shù)值空間。這也使得主鍵字段比較大,在關(guān)聯(lián)查詢的時(shí)候有一定的性能損失,判斷GUID值是否相同總比判斷int值是否相同要多費(fèi)些功夫。
在實(shí)際應(yīng)用中到底該用什么方案來產(chǎn)生對(duì)象標(biāo)識(shí)需要根具體情況決定,以上方案僅僅是理論探討。
接下來要注意的就是主鍵的索引結(jié)構(gòu)的優(yōu)化了。
主鍵都要整成聚集索引。聚集索引在SQL Server內(nèi)部就是聚集表,聚集表是B樹結(jié)構(gòu),索引值存在B樹的中間節(jié)點(diǎn)中,而數(shù)據(jù)行就存放在B樹的頁節(jié)點(diǎn)上。也就是說,聚集索引和數(shù)據(jù)表其實(shí)是一個(gè)統(tǒng)一的整體結(jié)構(gòu)。因此,聚集索引查找和定位數(shù)據(jù)的效率要比一般索引高出很多。
此外,專門主鍵字段值是永遠(yuǎn)都不變。聚集索引建值不變,聚集表的節(jié)點(diǎn)也不會(huì)調(diào)整,硬盤上的數(shù)據(jù)記錄塊基本不動(dòng)窩的。這可以極大減少數(shù)據(jù)庫碎片,除非刪除數(shù)據(jù)行。數(shù)據(jù)庫的碎片少了,查詢數(shù)據(jù)自然要快些。
一般來說,我們存入數(shù)據(jù)庫中的數(shù)據(jù)總是有時(shí)間順序的,我們?nèi)粘2樵兒褪褂玫臄?shù)據(jù)也總是近期的數(shù)據(jù)。有的常用查詢甚至總是按時(shí)間順序倒排,比如,郵件列表,論壇帖子。如果主鍵采用的是自增字段,我們不妨將增量值設(shè)為-1或干脆搞成倒序的主鍵。這樣,查詢的排序與掃描索引的順序相同,畢竟硬盤的磁頭總喜歡從前往后讀,這會(huì)稍微提高一點(diǎn)讀取聚集索引的速度。
同樣,如果使用隨機(jī)主鍵值的方案,我們也建議采用與時(shí)間相關(guān)的隨機(jī)數(shù)值,而不是GUID。與時(shí)間相關(guān)的隨機(jī)數(shù)就是,雖然主鍵是隨機(jī)產(chǎn)生的,但后產(chǎn)生的隨機(jī)數(shù)應(yīng)該大于先產(chǎn)生的數(shù)。
為什么不用GUID呢?因?yàn)樗傻闹稻拖耦B皮的猴子,到處亂跳。于是,在每次插入記錄時(shí),聚集索引節(jié)點(diǎn)中相鄰的值并不具有時(shí)間上的順序。而當(dāng)我們習(xí)慣性地查詢近期數(shù)據(jù)時(shí),硬盤的磁頭也需要像猴子般亂跳一通之后才能讀到按時(shí)間順序的所有數(shù)據(jù)。
如果采用有時(shí)間順序的隨機(jī)值,聚集索引插入數(shù)據(jù)時(shí)總往一個(gè)方向增加數(shù)據(jù)行的頁節(jié)點(diǎn),與同一時(shí)期的數(shù)據(jù)行幾乎總相鄰。查詢同期數(shù)據(jù)時(shí),磁頭就很少亂跳了。磁頭一次可以讀取一批數(shù)據(jù),效率有將有所提升。如果您用電子顯微鏡去觀察硬盤的表面,聚集索引的那顆B樹將生長(zhǎng)得很正很齊。
對(duì)磁頭讀寫的優(yōu)化,是完美主義的程序員所追求的,是否采用完全看自己的心態(tài)。總之,高興就好。當(dāng)然,等將來的大容量存儲(chǔ)設(shè)備都用上固態(tài)盤了,沒磁頭了,全電信號(hào)了,這些優(yōu)化就都沒有什么意義了。
所以呢,追求完美也是很痛苦的。吃了程序員這碗飯,就只能被IT的洪流卷著往前走。我們不過是這洪流中的一粒沙子,不知道又會(huì)被帶到何方?
王菲在一首歌中這樣唱到:一路上有人太早看透生命的線條命運(yùn)的玄妙,有人太晚覺悟冥冥中該來則來無處可逃...
原創(chuàng)作者:李戰(zhàn)(leadzen).深圳 2008-5-10
原文地址:http://www.cnblogs.com/leadzen/archive/2008/05/10/1191010.html
【轉(zhuǎn)載請(qǐng)注明作者及出處】
it知識(shí)庫:主鍵的故事,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。