【译】索引进阶(十一):SQL SERVER中的索引碎片【上篇】
原文鏈接:傳送門。
?
第十章節我們分析了索引的內部結構。有了這些關于索引結構的知識,我們便可以分析索引碎片了:其產生的原因,如何防止,以及何時可以不去關注它們。
一些背景知識 / 復習
以下知識對于理解索引碎片來說是至關重要的,有些知識在之前的章節中都已經出現過,比如使用索引來返回數據的場景。在本節,我們的場景是索引的維護,因此,一些舊的知識在此會重復出現,而同時會增加一些新的知識。
每張表要么是個堆要么是個聚集索引。
如果表是個堆,那么其非聚集索引的書簽部分便是一個行標識符(RID),如果表是一個聚集索引,那么非聚集索引的書簽部分便是其聚集索引的索引鍵值。
索引條目存儲在頁中,8個物理上連續的頁組成一個區。
索引層級從底部,從0開始計算,因此葉子層級總是第0層,而最低的中間層節點總是第1層,根頁總是自己成為一個層級,并且它具有最高的層級數。
掃描整個索引意味著葉子層級的所有頁都會被讀且僅會被讀一次。每一個頁包含了一個指向上一個和下一個頁的指針。頁的邏輯順序和物理順序或許是不一致的。指向下一個頁的指針后續會指向位于同一個區的頁,或者指向幾百個頁之前的一個頁(也可能是之后),甚至于指向不同文件的一個頁。
頁的物理順序和邏輯順序越一致,掃描整個索引所需要的IO數就越少。當兩個順序一致的時候,SQL SERVER每次IO可讀取一整個區或者更多。同樣的,頁的物理順序和邏輯順序越相關,那么使用的預讀數便越多(預讀:在使用之前,先將頁存入內存)。不管是掃描整個索引,還是只掃描索引的一部分,這都是確定無疑的。
為了返回一個索引條目,從根節點開始,每個層級的一個頁都必須被訪問到。這種操作的性能不會被索引物理順序和邏輯順序的相關性所影響。
記住了這些信息,我們來檢查索引碎片這個主題。
什么是碎片?
索引碎片來自于兩種情形:內部碎片和外部碎片。決定索引碎片(不管是內部碎片或者是外部碎片)的最好工具是?sys.dm_db_index_physical_stats這個動態管理函數。因為這個函數顯示的是索引ID,而不是索引的名稱,查詢常常將它與視圖sys.indexes 連接起來,從而在輸出中包含索引名稱,本章節我們將使用這個函數,首先我們梳理下內部碎片,然后再陳述下外部碎片。
?內部碎片
每個頁都會包含一定數量的索引條目,那并不意味著每個頁總是包含最大數量的條目數。為了本章后續會講到的一些理由,通常一個索引頁并不總是完全充滿的。當我們說一個索引包含內部碎片時,我們就意味著這個頁并不是完全充滿的。在一個索引中,每頁占據的空間的數量體現了索引的內部碎片,同時也體現了一個索引的平均頁覆蓋率。注意覆蓋率測量得越高,內部碎片便越少。一個100%充滿的頁是每有內部碎片的。內部碎片通常以一個百分比的形式被報告出來,并且其體現了字節數上的覆蓋率而不是條目上的覆蓋率。因此一個覆蓋率是96%的索引頁或許是完全充滿的。那就是說,4%的頁空間或許不是足夠的空間用來添加一個新的條目。當頁的空間被頭信息及頁的偏移指針占據的時候,一個各個條目相對比較大的頁或許在90%,80%,甚至更少的時候就會充滿。
sys.dm_db_index_physical_stats函數在它的avg_page_space_used_in_percent輸出列報告了索引的內部碎片。顯示在列表1的查詢,它檢查了SalesOrderDetail表的聚集索引,顯示了其內部碎片信息。
SELECT IX.name AS 'Name', PS.index_level AS 'Level', PS.page_count AS 'Pages', PS.avg_page_space_used_in_percent AS 'Page Fullness (%)'FROM sys.dm_db_index_physical_stats( DB_ID(), OBJECT_ID('Sales.SalesOrderDetail'), DEFAULT, DEFAULT, 'DETAILED') PSJOIN sys.indexes IXON IX.OBJECT_ID = PS.OBJECT_ID AND IX.index_id = PS.index_id WHERE IX.name = 'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID'; GO?列表1:? 一個返回內部碎片信息的查詢
?
這個查詢對于索引的每個層級都會返回一行輸出,如圖表1中所顯示的。
?圖表1:內部碎片查詢的輸出
這個特定查詢的主題是聚集索引,因此索引的頁層級是表的數據行,第0層的輸出,也就是以上輸出的第一行,告訴我們表的數據行分散在1234個頁中,其平均充滿率是99%。換句話說,這個表具有很小數量的內部碎片。
外部碎片
與內部碎片相對的,外部碎片指的是一個索引邏輯順序與物理順序的不一致。它同樣被報告為一個百分比。引用微軟技術網的原文,它指的是一個索引的葉子節點中無序頁(out-of-order pages)的百分比,所謂的無序頁指的是如果當前頁物理上分配的緊鄰的頁不是偏移指針所指向的頁。那么當前頁便稱之為無序頁。雖然官方定義將定義本身限制在葉子層級,你將注意到?sys.dm_db_index_physical_stats函數能夠返回索引所有層級的碎片信息。
我們注意到,與內部碎片不同的是,一個高的數值意味著更大數量的外部碎片,因此一個具有100%外部碎片的索引幾乎完全是外部碎片,也就是說,它的邏輯順序與物理順序完全沒有任何關聯性。
考慮圖片2演示的一個簡單的例子。一個16個頁的索引占據了起始于數據庫文件40頁和56頁的區。除了每個區的最后一個頁,任何一個頁的下一頁指針指向區中的下一頁。而第一個區的最后一頁指向第二個區的第一頁。第二個區的最后一頁沒有指向任何頁,因為其是索引的最后一個頁。
在這個例子中,無序頁的數量是0,雖然第8,9頁在文件中是物理上不連續的,它們在分配給索引的空間內是連續的,因此這個例子的外部碎片是0。
? ? 圖2: 沒有外部碎片的索引
在另一方面,圖3展示了一樣的表,但是具有一些無序頁,前三個頁邏輯上是連續的,但是在那之后其邏輯順序和物理順序便沒有任何相關性。
? ? 圖3:具有外部碎片的索引
任何下一頁指針如果沒有指向索引的物理上分配的下一個頁的話,便是一個無序指針,不管其向前指,向后指,在區內,跨區,甚至于跨越整個文件。
為了演示如何獲取一個索引的外部碎片信息,我們將之前的查詢做兩處改動。因為外部索引涉及了頁之間的關系,而不是頁的內容,它能夠被掃描索引的第一層定義出來,而不用掃描大得多的索引的葉子層級。我們將函數的最后一個參數從DETAILED 修改為LIMITED (or DEFAULT),從而告知SQL SERVER葉子節點掃描不是必要的。
同時我們修改SELECT子句包含一些稍微不同的列集,它們能夠提供索引的外部碎片信息,最終修改后的查詢顯示如下:
SELECT IX.name AS 'Name' , PS.index_level AS 'Level' , PS.page_count AS 'Pages' , PS.avg_fragmentation_in_percent AS 'External Fragmentation (%)' , PS.fragment_count AS 'Fragments' , PS.avg_fragment_size_in_pages AS 'Avg Fragment Size' FROM sys.dm_db_index_physical_stats( DB_ID(), OBJECT_ID('Sales.SalesOrderDetail'), DEFAULT, DEFAULT, 'LIMITED') PS JOIN sys.indexes IX ON IX.OBJECT_ID = PS.OBJECT_ID AND IX.index_id = PS.index_id WHERE IX.name = 'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID'; GO因為我們給最后一個參數指定了LIMITED,我們將得到一行輸出,如圖4所示。
? 圖4:外部碎片查詢輸出
?這個結果告訴我們這個索引的大小是1234頁,包含了20個碎片,平均每個碎片的大小是61.7頁。如同砍一塊木頭導致兩塊,砍兩下導致3塊,一個無序的下一頁指針導致兩個碎片,兩個無序指針導致三個碎片,等等。因此我們的索引含有20-1 =19個頁包含無序指針,平均下來,當以邏輯數據掃描這個索引的時候,在各個無序頁之間61個連續的頁會發生。這是一個包含很少外部碎片的索引。
SSMS的索屬性窗口,顯示在圖5,通過執行一個與我們之前執行的查詢類似的查詢來獲取它的信息,其Page fullness和Total fragmentation分別指的是索引的內部碎片和外部碎片。
? ?圖5:SSMS的索引屬性窗口
本節我們介紹了索引碎片的基礎知,下一節我們將深入討論索引碎片,包括其如何產生,如何防止,以及一些通用的實踐模式。To be continued...
?
轉載于:https://www.cnblogs.com/qianxingmu/p/10633503.html
總結
以上是生活随笔為你收集整理的【译】索引进阶(十一):SQL SERVER中的索引碎片【上篇】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux的less命令如何退出,Lin
- 下一篇: mysql isnull