SQL Server索引进阶第六篇:书签
SQL Server索引進階第六篇:書簽
?
索引設計是數據庫設計中比較重要的一個環節,對數據庫的性能其中至關重要的作用,但是索引的設計卻又不是那么容易的事情,性能也不是那么輕易就獲取到的,很多的技術人員因為不恰當的創建索引,最后使得其效果適得其反,可以說“成也索引,敗也索引”。
?
?
本系列文章來自Stairway to SQL Server Indexes,然后經過我們團隊的理解和整理發布在agilesharp,希望對廣大的技術朋友在如何使用索引上有所幫助。
?
?
系列文章索目錄:
SQL Server索引進階第一篇:索引介紹
SQL Server索引進階第二篇:深入非聚集索引
SQL Server索引進階第三篇:聚集索引
SQL Server索引進階第四篇:頁和區
SQL Server索引進階第五篇:索引包含列
SQL Server索引進階第六篇:書簽
SQL Server索引進階第七篇:過濾的索引
SQL Server索引進階第八篇:唯一索引
SQL Server索引進階第九篇:解讀執行計劃
SQL Server索引進階第十篇:索引的內部結構
SQL Server索引進階第十一篇:索引碎片分析與解決(上)
SQL Server索引進階第十一篇:索引碎片分析與解決(中)-碎片發生原理深度剖析
SQL Server索引進階第十二篇:索引的創建,修改和刪除
SQL Server索引進階第十三篇:Insert,Update,Delete語句
SQL Server索引進階第十四篇:索引統計
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 建立含有包含列的非聚集索引
?
?在上面索引中,部分數據的順序如下所示。
?
9/9/2012 11:16:11 AM
?
?
?
??? 上面建立的非聚集索引因為使用了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所示的非聚集索引一樣的索引。唯一的不同是現在這個版本是基于聚集索引而不是堆的。非聚集索引的部分數據如下:
?
9/9/2012 11:16:11 AM
?
?
我們現在有了兩個版本的非聚集索引,分別是創建在堆上的非聚集索引和創建在聚集索引上的非聚集索引,唯一的不同就是它們的書簽值。
?
哪種更好
?
???? 上面兩種聚集索引是不是一種要比另一種更好呢?或許吧,但是也要看具體情況。基于RID的書簽允許快速的找到底層表中行所在的物理位置,而基于聚集索引的書簽找到底層表的行就慢多了,但這個書簽還可以作為包含列使用,書簽列同時也常常會被當作外鍵。
?
???? 所以對于上面哪種非聚集索引更好的真正答案是”都不是”。當在表上建立索引時,最重要的選擇只是使用哪些列作為索引鍵。一旦你確定了聚集索引列(基于文中所示的三點建議),剩下的非聚集索引所帶來的影響就交給SQL Server來處理吧。
?
小結
?
??? 非聚集索引包含了索引鍵列,包含列和書簽。書簽的值根據所在表是堆還是聚集索引既可以是RID也可以是聚集索引鍵。對于表上聚集索引的最好選擇要基于文中所給的三點指南。
?
轉載于:https://www.cnblogs.com/lteal/archive/2012/12/03/2799283.html
總結
以上是生活随笔為你收集整理的SQL Server索引进阶第六篇:书签的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [转载]从零开始学习jQuery (一)
- 下一篇: M2第五天DailyScrum——PM(