SQL Server索引進階第十一篇:索引碎片分析與解決(上)

相關有關索引碎片的問題,大家應該是聽過不少,也許也很多的朋友已經做了與之相關的工作。那我們今天就來看看這個問題。爲了更好的說明這個問題,我們首先來普及一些背景知識。


知識普及


我們都知道,數據庫中的每一個表要麼是堆表,要麼就是包含聚集索引的表,或者我們稱之爲有序表。如果表是一個堆表,那麼在使用非聚集索引查詢數據的時候,會使用書籤查找去底層的數據表中去檢索需要的數據,這個書籤查找會通過每一個索引中包含的行標識(RID)去定位每一個底層數據表的數據行。如果表上面有聚集索引,那麼在使用非聚集索引查找其他需要數據的時候,就會使用聚集索引鍵去定位底層的數據行。


我們也知道,索引是由索引頁組成的,索引中的每一個條目包含在頁中。每8個頁組成一個塊。


索引的層級是從底向上的,就是一個樹結構,最下面的就是第0層,也是葉節點。索引中的根節點處於整個索引的最上層。


如果要掃描整個索引,那麼就意味着必須要讀取頁節點中的每一個頁(要麼是數據頁,要麼是索引頁)。其中,每個頁都包含着一個指向它前面的頁和一個指向它後面也的指針。之前,我們也提過:如果單看某一層節點,其實就是一個雙向鏈表。還是上個圖,大家感受一下。



我們應該知道:頁(不管是數據頁,還是索引頁 ,還是其他的類型的頁)處於的邏輯順序和它的物理順便不一定就是一樣的,也就說,在A頁中的指針指向了它的下一個頁B,也就說A和B頁在邏輯上面是一起的,但是它們在物理上面可能不一樣,甚至B頁和A頁在物理上相隔幾百個頁。


如果在邏輯上面相連的頁在物理存儲級別相隔的越近,那麼在讀取這些頁的時候所花的I/O成本也就越小,因爲產生磁盤的磁頭移動帶來的延遲。相反,如果他們的物理存儲順序和邏輯順序一致,那麼SQL Server在讀取的時候,就可以一次讀取,因爲每次會讀取一個塊(8個頁)。

好了,普及知識之後,我們就來看看什麼是碎片。


什麼是索引碎片


索引碎片可以分爲兩類:內部索引碎片和外部索引碎片。下面我們就來具體的看看而這之前的區別以及如何檢查。


內部索引碎片


每一個索引頁中都包含一些索引的條目(就類似數據頁包含很多的數據行一行),這一點我們在之前講過了的。但是,很多的時候,不是每個頁都包含了最大的條數。例如,一個頁的大小8k,也就是4096字節,除去一些頁頭,頁腳等,還剩下8000多字節,如果每個索引條目的大小事100字節,那麼這個索引頁最大就可以包含80個條目,但是很多的情況下,卻沒有包含這麼多。


也就說,很多的時候,索引頁並沒有完全的填滿,或者這是問題,或許這麼我們特意這樣的,我們後續會提到。當我們談到索引碎片的時候,我們往往就是指這些索引頁沒有完全填滿。或者說的更加明白一點就是:我們原本是希望頁都被填滿的,但是隨着數據的增刪改,使得索引中的數據沒有填滿,結果如下:



圖不是很清晰,大家意會一下就行了。


我們可以使用
sys.dm_db_index_physical_stats來查看相關的內部碎片的情況,執行查詢如下:

  1. SELECT IX.name AS 'Name'

  2. , PS.index_level AS 'Level'

  3. , PS.page_count AS 'Pages'

  4. , PS.avg_page_space_used_in_percent AS 'Page Fullness (%)'

  5. FROM sys.dm_db_index_physical_stats( 

  6. DB_ID(), 

  7. OBJECT_ID('Sales.SalesOrderDetail'), 

  8. DEFAULT, DEFAULT, 'DETAILED') PS

  9. JOIN sys.indexes IX

  10. ON IX.OBJECT_ID = PS.OBJECT_ID AND IX.index_id = PS.index_id
複製代碼


執行結果如圖:



我們可以看到每個索引的頁面的填充情況。

下面,我們再來講講外部索引碎片。


外部索引碎片


理解了上面的問題,這個外部索引碎片就好理解了,最簡單的說法就是:索引中的索引頁的邏輯順序和物理順序不一致。我們通過個圖對比的來看看。



在上圖中,一個索引包含了16個頁。但是這16頁不是包含在2個相連的塊中的,而是分佈在不同的地方,因爲它們之前中的一些塊被其他的對象佔用了。這樣就導致了16個頁在物理上面不連續,這就是碎片。在讀取的時候,就會消耗額外的I/O。


和之前一樣,我們可以使用
sys.dm_db_index_physical_stats來查看外部碎片的情況。但是這裏的參數值可能要發生變化了:之前在sys.dm_db_index_physical_stats最後一個參數值是'DETAILED',這裏我們的值是LIMITED或者Default。因爲外部碎片關注的是索引頁之前的連續性問題,不關注每一個頁中的數據,此時只是部分的掃描,沒有必要全部的掃描。大家可以參看MSDN的去進一步的理解這些參數的含義。


查詢如下:

  1. SELECT IX.name AS 'Name'

  2. , PS.index_level AS 'Level'

  3. , PS.page_count AS 'Pages'

  4. , PS.avg_fragmentation_in_percent AS 'External Fragmentation (%)'

  5. , PS.fragment_count AS 'Fragments'

  6. , PS.avg_fragment_size_in_pages AS 'Avg Fragment Size'

  7. FROM sys.dm_db_index_physical_stats(

  8. DB_ID(),

  9. OBJECT_ID('Sales.SalesOrderDetail'),

  10. DEFAULT, DEFAULT, 'LIMITED') PS

  11. JOIN sys.indexes IX

  12. ON IX.OBJECT_ID = PS.OBJECT_ID AND IX.index_id = PS.index_id
複製代碼


結果如下:



除了使用腳本之外,我們還可以在SQL Server管理器中查看,在某個索引上面右鍵,屬性,如下:


好,我們下一篇文章就來進一步的談談一下的內容:

是什麼導致了碎片

如何解決碎片問題

後文更加精彩!!!

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