SQL server 容易讓人誤解的問題之 聚集表的物理順序問題

 

 對於MS SQL server 數據庫,有幾個容易讓人產生誤解的問題,對於這幾個問題,即使很多 SQL server DBA 都有錯誤認識或者認識不充分,所以我想撰文幾篇,把這些容易理解錯誤的問題前前後後深入闡述一下,也希望糾正一下網上對這幾個問題的訛傳(我也可能有認識不對的地方,歡迎批評指正)。

      第一個問題:聚集表的物理順序問題。這個問題很有迷惑性,因爲很多教科書在講到聚集索引的時候都會出現“聚集索引是按照聚集鍵的排序順序物理地存儲數據” 類似的說法,因此我們很容易產生以下幾種誤解:

      誤解一:“按順序物理地存儲”就是磁盤本身;

      誤解二:聚集表的頁在物理上是順序的;

      誤解三:聚集表的頁在物理上是順序的,並且是連續的。

 

     下面我們分別說明上面三種觀點確實是誤解。

     對於誤解一,我們必須要了解數據行如何被存儲在數據頁上。

              

上圖是我從MSDN上截來的,從上圖可以看到,在每個數據頁的末尾,都有一個“行偏移量”的數組,這個數組記錄了每一個數據行的開頭在頁面中的起始位置,即每行數據開頭應該從頁頭偏移多少個字節。我將上圖改了一下,可能更便於正確理解:

           

我改編的這個圖中表示,從這個數據頁頭偏移96個字節即是第一行數據的開始位置,偏移200字節即是第二行數據的開始位置,偏移300個字節即是第三行數據的開始位置。

 

下面我們舉個實際的例子來說明聚集表的數據在一個數據頁上可能的樣子。

複製代碼
CREATE TABLE test
(
   RowId   int not null primary key ,
   Column1 char(100)
)

 
INSERT INTO test(RowId,Column1)
Select 1, ' '
Union
Select 2,' '
Union
Select 10,' '
複製代碼

對於上表,數據第一次插入時,這三行數據在數據頁上的表現如下:

       

而如若我再往裏面插入如下數據:

INSERT INTO test(RowId,Column1)
Select 7, ' '

因爲這個表的RowId 列有聚集索引(primary key 默認創建聚集索引),而數字7大約2,且小於10,那麼SQL server 在執行INSERT 操作時,是不是會把RowId = 10 這行數據往下挪呢?顯然SQL server 不會這麼笨,而只會將RowId = 7這行數據數據附加到RowId = 10 這行數據的後面,然後再修改行偏移量數組,示意圖如下:

          

      現在我們應該可以明白:一個聚集索引表數據頁上數據行的物理順序,僅依靠行偏移列表來決定,並不取決於在磁盤上的物理位置。

 

     對於誤解二和誤解三可以放在一起論述,在論述之前,我們需要先了解一下SQL server 存儲引擎中頁跟區的概念。
         1. SQL server 中數據存儲的基本單位是頁。
         2. 區是八個邏輯上連續的頁的集合,用來有效的管理頁,這也說明,所有的數據頁一定屬於某個區。
         3. 區分爲混合區和統一區。混合區中的頁可以被分配給多個數據庫對象;統一區中的頁一定是被分配給了某一個數據庫對象。
         4. SQL server 在爲某個數據庫對象申請空間時,需要使用GAM,SGAM,PFS 系統頁的信息,同時在空間被分配後,也會維護好GAM,SGAM,PFS系統頁的息                      。

         上面我簡單列出了幾個要點,更多詳細信息大家可以參考下面的鏈接信息:
         http://msdn.microsoft.com/zh-cn/library/cc280360(v=sql.100).aspx 

         然後我將列舉具體的情況來證明二跟三確實是誤解:
         1. 當我們新建一個聚集表,並且往表裏插數據,當這個表所佔用空間不夠8個數據頁時,SQL server存儲引擎都將從混合區尋找空閒的頁面分配給表。而在這個階段內,很容易出現一個混合區被同時分配給多個數據庫對象(最多可達8個數據庫對象)。那麼,如果我們的聚集表需要再次申請磁盤空間,就很可能在起初的混合區內分配到不連續的頁(相對於已分配給這個聚集表的頁來說),或者要從另外一個混合區查找空閒頁面,這樣,是無論如何也保證不了“聚集表的頁在物理上是順序的”,更保證不了“頁是連續的”。

         2. 如果一個聚集表滿8個頁,那麼,後續所有的空間申請都將分配完整的統一區,這後續的統一區是否可以順序,或者連續呢? 當然不行,當一個申請空間的動作發出後,存儲引擎都會從表所在數據文件的第一個GAM去遍歷查找空閒的統一區,如果某個統一區在之前已經分配給某個其它的數據庫對象,但當前這個區已經被釋放,那麼,SQL server 就會將這個空閒的區分配給我們的聚集表,這樣也自然保證不了頁在物理上的“順序和連續”;另外,即使SQL server每次都分配從來沒有分配給任何數據庫對象的區給我們的聚集表,也沒有辦法保證頁在物理上的“順序和連續”。

         3. 如果聚集表出現分頁的情況,那麼新申請頁的頁面鏈的“上一頁”會指向被分割的數據頁,“下一頁”會指向被分割頁在分割前指向的下一頁。顯然,這種情況也沒有辦法保證頁在物理上的“順序和連續”,而且它是索引碎片情況中的一種。

 

結論:對於聚集索引表數據行的物理順序問題,在頁與頁的角度來看,唯一能指明聚集索引表數據頁順序的是數據頁上的頁面鏈表。因爲頁面鏈表清楚的指明瞭本頁的上一頁,及下一頁的頁面號分別是啥,而頁面號就決定了上一頁及下一頁的物理位置;如果將眼光縮小到一個數據頁的範圍內,決定數據行物理位置的因素只有頁腳的行偏移量數數組。

 

發佈了14 篇原創文章 · 獲贊 23 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章