SQL Server索引進階第六篇:書籤

書籤是什麼




    我們已經在前面提到過書籤,但僅僅說了書籤可以幫助SQL Server快速從非聚集索引條目導向到對應的行,本篇文章開始讓我們對書籤的探索更進一步.書籤的內容實際上是取決於非聚集索引所在表是以堆還是聚集索引存放的。




     不論表是堆結構還是段結構,可以確定的是,表中每一行都是某一頁的第N行,這個某一頁又是某個數據庫文件的第N頁,這個某個數據庫文件又是構成數據庫的文件組的第N個文件,因此,數據庫中的每一行,在指定時間都可以由三個數字進行定位:  文件號:頁號:行號。這三個數字組合起來就是所謂的RID。很多顯示SQL Server內部結構的工具軟件都會將這三個數字通過冒號分隔進行顯示。比如,文件1的第77頁的第12行的RID就是1:77:12。




    通常來說,在堆上的行不會被改變位置。一旦它們被插入某個頁中,它們就會一直呆在那。如果要用更嚴謹的技術術語來說的話:行在堆上的行很少移動。如果行被移動的話,它們會在原來的位置留下指向其移動到的位置的指針。而由聚集索引組織的行,是可以被移動的,行在改動數據或是整理索引的時候要被移動位置。更多的細節會在本系列文章後續篇幅進行介紹。




    因爲在堆上的行幾乎很少移動,所以RID就可以唯一標識某一行。RID的值不僅僅不變,RID所表示行的物理位置也不會變。這使得RID的值更適宜作爲書籤。這也是爲什麼SQL Server在堆上建立的非聚集索引的書籤都使用RID。




堆上的非聚集索引:基於RID的書籤 



    假如SalesOrderDetail是一個基於堆的表;表中的每行都不是有序的。下面讓我們建立以ProductID/ModifiedDate爲非聚集索引鍵幷包含OrderQty, UnitPrice, LineTotal三個列的非聚集索引,如代碼6.1所示。





CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate 
  
ON Sales.SalesOrderDetail(ProductID, ModifiedDate)



INCLUDE (OrderQty, UnitPrice, LineTotal)


代碼6.1 建立含有包含列的非聚集索引 



 在上面索引中,部分數據的順序如下所示。






 


 


    上面建立的非聚集索引因爲使用了RID作爲書籤,直接指向對應行所在的物理地址,因此十分高效。但雖然RID值用於鍵查找非常高效,但書籤中包含的值和具體的用戶數據無關。 



     另一種與這種基於RID的書籤不同的書籤,是當非聚集索引所在的表包含聚集索引時出現的。更準確的說,這種書籤是建立聚集索引上的非聚集索引的書籤。





在聚集索引上的非聚集索引:基於鍵值的書籤 


    如果表是基於聚集索引的,則表內數據可以在表移動。因此,對於聚集索引來說,RID並不能一直不變的定位一個相同的行。因此必須用另外的方法定位行,這個方法就是使用聚集索引的索引鍵。 



    使用聚集索引鍵作爲書籤可以使得當數據在頁中的行改變時,不需要非聚集索引的書籤的值進行變動,因此非聚集索引的鍵就可以用於去找底層表的數據,意思是根據書籤取數據不再基於物理位置,而是基於聚集索引查找。




    然而,以聚集索引鍵作爲非聚集索引的書籤最好要聚集索引鍵滿足如下標準:


 


 



    索引應該具有唯一性. 每一個索引條目書籤都應該使得書籤可以通過聚集索引的鍵值唯一的確認表中的一行,如果你創建的聚集索引鍵值不唯一,SQL Server將會爲有重複鍵值的每一行自動加上一個叫uniquifier的東西使得每一行唯一。這個uniquifier對客戶端是透明的。對於是否可以允許聚集索引鍵重複,你需要考慮以下兩點:




  •     生成uniquifier增加SQL Server插入操作的額外負擔,在插入時SQL Server還需要判斷插入的值在表中是否唯一,如果不唯一生成uniquifier值再進行插入。 



  •     uniquifier本身對業務數據來說是沒有意義的,但是這個uniquifier本身不僅僅需要佔用聚集索引鍵的空間,還同時佔用非聚集索引書籤的空間。 

    索引鍵應該短.索引鍵所佔的字節數應該短.因爲這個鍵還會佔用非聚集索引書籤的空間。比如Contact表中以Last name / first name / middle name / street組合作爲索引鍵看上去不錯,但如果表中存在多個非聚集索引的話情況就有些微妙了。n個非聚集索引使得Last name / first name / middle name / street這些字段被存儲在n+1個位置。




    索引鍵最好不要變動.也就是索引鍵的值最好不要變動。對於聚集索引鍵的修改會使得基於這個聚集索引的所有非聚集索引同樣進行修改。所以對於聚集索引的一次update會造成n個非聚集索引書籤的update+1個聚集索引鍵值本身的update。



    AdventureWorks的設計團隊在設計SalesOrderDetail表的聚集索引時就是完全遵循上面的建議。它們選擇SalesOrderID / SalesOrderDetailID作爲聚集索引鍵完全滿足了窄,短和唯一的要求。將SalesOrderID作爲索引鍵最左邊的一列,儘管SalesOrderDetailID是唯一的,這兩列組合在一起進行聚集。以SalesOrderID / SalesOrderDetailID作爲主鍵和聚集索引鍵,就不再需要單獨建立SalesOrderDetailID的非聚集索引了。 



    現在我們創建和列表6.1所示的非聚集索引一樣的索引。唯一的不同是現在這個版本是基於聚集索引而不是堆的。非聚集索引的部分數據如下:









我們現在有了兩個版本的非聚集索引,分別是創建在堆上的非聚集索引和創建在聚集索引上的非聚集索引,唯一的不同就是它們的書籤值。





哪種更好 



     上面兩種聚集索引是不是一種要比另一種更好呢?或許吧,但是也要看具體情況。基於RID的書籤允許快速的找到底層表中行所在的物理位置,而基於聚集索引的書籤找到底層表的行就慢多了,但這個書籤還可以作爲包含列使用,書籤列同時也常常會被當作外鍵。




     所以對於上面哪種非聚集索引更好的真正答案是”都不是”。當在表上建立索引時,最重要的選擇只是使用哪些列作爲索引鍵。一旦你確定了聚集索引列(基於文中所示的三點建議),剩下的非聚集索引所帶來的影響就交給SQL Server來處理吧。





小結 


    非聚集索引包含了索引鍵列,包含列和書籤。書籤的值根據所在表是堆還是聚集索引既可以是RID也可以是聚集索引鍵。對於表上聚集索引的最好選擇要基於文中所給的三點指南。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章