惟一索引和非惟一索引的區別簡析

SQL Server建立索引時,能夠指定Unique使之成爲惟一索引。「惟一」顧名思義,可是兩都到底有什麼區別呢?由於索引也是一種物理結構,因此仍是要從存儲和結構上分析。sql

索引結構分葉級非葉級,分析時咱們要分開來看,這個很重要。數據庫

文中涉及的索引行大小計算,參考MSDN估計數據庫大小索引部分。ide

1. 非惟一彙集索引和惟一彙集索引測試

  建立兩個測試表,各10000條整數,tb1惟一,tb2非惟一,有1000條爲9999的重複值。this

Code

 

先查詢索引的一些基本情況:spa

image

從上面的結果能夠看到,不管是葉級仍是非葉級,非惟一彙集索引的索引行都比惟一的大一些,因此所佔頁也多一點。固然,由於測試數據很小,又是int,因此不明顯。設計

 

那到底大在哪裏呢?將二者的非葉級頁和葉級頁放在一塊兒比一下就知道了。先找出頁號,再用DBCC PAGE來查看。3d

經過Paul S. Randal寫的存儲過程sp_allocationMetadata能夠查到根頁和每級索引的首頁。指針

image

 

就挑這兩個頁作對比。code

image

發現多出一個UNIQUIFIER,一樣葉級也是同樣。MSDN說明:

若是彙集索引不是惟一的索引,SQL Server 將添加在內部生成的值(稱爲惟一值)以使全部重複鍵惟一。此四字節的值對於用戶不可見。僅當須要使彙集鍵惟一以用於非彙集索引中時,才添加該值。」

還有UNIQUIFIER不是一個全局自增列,重複記錄增長時此值會發生改變,而且它是一個可爲null的變長列。

如今來算一算索引行大小:

   兩個表都是隻有一個int型可爲NULL的字段,而彙集索引葉級是存儲數據自己

   葉級是一個4字節的INT列,無變長列,加上3字節的NULL位圖,再加上4字節的行頭開銷:兩個表的葉級minSize =4+0+3+4=11

   非葉級是一個4字節的INT列,無變長列,加上3字節的NULL位圖,加上1字節的行頭開銷,再加6字節的子頁指針:兩個表的非葉級minSize=4+0+3+1+6=14

   tb1的索引行大小是一致的minSize=maxSize,由於它是惟一的。tb2的索引行大小不一致,有大有小,大的索引行是由於:a)不惟一 b)UNIQUIFIER

   惟一標識列增長了2+1*2+4=8字節開銷,tb2的min和max相差就是這8字節。

   tb2的葉級maxSize=4+8+3+4=19

   tb2的非葉級maxSize=4+8+3+1+6=22

小結:非惟一彙集索引爲保證索引鍵值惟一性,會生成UNIQUIFIER與鍵列一塊兒組成索引鍵值。同時不管在葉級仍是非葉頁級,都比惟一索引佔用更多存儲空間。

 

2.堆表上的惟一和非惟一的非彙集索引

Code

image

二者的頁級大小是同樣的,非葉級頁的相差8bytes。

 

跟1.中的分析方法同樣,挑兩個非葉級頁出來對比一下,看相差在哪裏。

image

非惟一索引行多了一個HEAP RID,MSDN說明:

若是非彙集索引不是惟一的,數據行定位符將與非彙集索引鍵組合使用,以便爲每一行生成惟一的鍵值。若是非彙集索引在堆上,則數據行定位符是堆 RID。其大小是 8 個字節。

二者葉級索引行大小=INT型4字節+無變長列+1字節行頭+3字節NULL位圖+8字節RID=4+0+1+3+8=16

惟一索引的非葉級行=INT型4字節+無變長列+1字節行頭+3字節NULL位圖+6字節子頁索引=14

非惟一索引的非葉級行=INT型4字節+無變長列+1字節行頭+3字節NULL位圖+6字節子頁索引+8字節的RID=22

小結:堆表上的非惟一索引在非葉級索引行上比惟一索引多出一列行定位符RID,而葉級是同樣的,都有RID列。因此非惟一要佔用更多的空間。

 

3.惟一彙集索引表上的惟一和非惟一非彙集索引 

跟2.中的測試數據同樣,只是把ID列改爲彙集主鍵。執行:

Code

image

這裏有意思的是彙集索引的非葉級行只有11字節,跟一樣的1.中的14相差了3字節。這3字節是由於如今這個表,索引列是自增主鍵,是不能爲NULL的,因此就沒有NULL位圖的3個字節的開銷了。

 

二者的葉級行大小同樣,看一下長的是否是同樣:
image

二者的區別在於對彙集索引鍵的引用上,即「id」和「id(key)」。MSDN說明:

若是非彙集索引不是惟一的,數據行定位符將與非彙集索引鍵組合使用,以便爲每一行生成惟一的鍵值。若是非彙集索引在彙集索引之上,則數據行定位符是彙集鍵。

惟一非彙集索引中的「id」是作爲行定位符引用的,非惟一非彙集索引中的「id(key)」,不只是作爲行定位符引用,而且仍是此索引自己鍵列(「key」的含義)。

非葉級行相差4個字節,對比一下頁的內容就知道差在哪裏:

image

從上圖看出非惟一索引比惟一的多了一列」id(key)」,這是前者引用了彙集索引鍵並作作本身的鍵列,而惟一索引不須要這樣。

小結:惟一彙集索引表上的非惟一非彙集索引與惟一非彙集索引,在葉級上大小是同樣的,在非葉級行多引用一列「彙集鍵」,因此前者佔用的存儲空間也會更大一些。

 

4. 非惟一彙集索引表上的惟一和非惟一非彙集索引

  測試數據中給製造了一些重複數據。

Code

image

由前文的分析可知上圖全部索引的minSize和maxSize相差8字節都是由UNIQUEIFIER產生。

兩個非彙集索引的葉級行同樣大,緣由跟3. 中分析的同樣,只是二者除了引用匯集鍵以外,還會引用跟彙集鍵結合使用的UNIQUEIFIER。

可是非葉級頁兩差有着較大的差別,查看頁面:

image

從頁面內容能夠看出,非惟一非彙集比惟一多了兩列,其它這兩列能夠當作一列,緣由就是當彙集索引不惟一時,會生成UNIQUIFIER並結合,用以保證彙集鍵惟一。

小結:非惟一彙集索引表上的非惟一非彙集索引在葉級行上大小是同樣的,而在非葉級行上前者比後大,因此也佔用更多存儲空間。

 

總結:

  1. 在表和索引設計階段,若是可能,字段設定爲不容許NULL,索引設定爲惟一。這樣節約存儲空間並提升了IO效率。

  2. 彙集索引鍵列應該儘可能選用窄的字段,由於非彙集索引會引用其鍵列。若是彙集鍵過大則會使非彙集索引同時也佔用更多存儲空間。

  3. SQL Server在建立索引時,默認是建立非惟一的。因此在建立索引時,要認真考慮是否能夠建立爲惟一索引。

  4. 文中用詞有些繞。

相關文章
相關標籤/搜索