SQL Server 2008儲存結構之GAM、SGAM

bq_wang發表於2010-10-27
談到GAM和SGAM,我們不得不從資料庫的頁和區說起。一個資料庫由使用者定義的空間構成,這些空間用來永久儲存使用者物件,例如資料庫管理資訊、表和索引。這些空間被分配在一個或多個作業系統檔案中。

  當我們建立一個資料庫的時候,例如以預設的方式CREATE DATABASE TESTDB,SQLServer自動幫我們建立好如下兩個資料庫檔案。

1
 

  這兩個資料檔案是實實在在的作業系統檔案,其中一個是叫行資料檔案,用來儲存資料庫的各種物件,另外一個是日誌檔案,從來記錄資料變化的過程。

  從邏輯角度而言,資料庫的最小儲存單位為頁即8kb。

   資料庫被分成若干邏輯頁面(每個頁面8KB),並且在每個檔案中,所有頁面都被連續地從0到x編號,其中x是由檔案的大小決定的。我們可以通過指定一個 資料庫ID、一個檔案ID、一個頁碼來引用任何一個資料頁。每個資料頁則用來儲存表和索引,以及相關的資料庫管理資訊。

  我們順著上面資料檔案的路徑可以找到該檔案,觀察一下新建的資料檔案的大小為:

  2.18 MB (2,293,760 位元組)=2,293,760b/8kb=280個頁面=35個區

  資料庫進行空間管理的最小單位為區(extents)。

  一個區由8個邏輯上連續的頁面組成(64KB的空間)。為了能夠更有效地分配空間,SQL Server 2008不會為少量的資料向資料表分配整區的空間。SQL Server 2008有兩種型別的區。

  統一型別的區 這些區為單個物件所有,區中所有的8個資料頁只能被所屬物件使用。

  混合型別的區 這些區能為最多8個物件共享。

  SQL Server為新的表或索引從混合型別的區中分配頁面。當該表或索引增長到8個頁面時,以後所有的分配都使用統一型別的區。

   當一張表或一個索引需要更多的空間時,SQL Server需要找到能夠用來分配的空間。如果該表或索引整體仍然少於8個頁面,SQL Server必須找到能夠用來分配的混合型別區構成的空間。如果表或索引有8個頁面或更大,SQL Server必須找到一個自由的統一型別的區。

  SQL Server使用兩種特殊型別的頁面來記錄哪些區已經被分配出去了,哪些型別(混合型別或統一型別)的區可供使用:

   全域性分配對映(Global Allocation Map,GAM)頁面 這些頁面記錄了哪些區已經被分配並用作何種用途。一個GAM頁面在它所覆蓋空間裡針對每一個區都有一個資料位。如果資料位為0,那麼對應的區正在使用;如 果該資料位為1,那麼該區為自由區。一個GAM頁面除了頁面頭部和其他一些需要記入的開銷大概有8 000位元組或者說64 000位空間可用,所以每個GAM頁面可以覆蓋64 000個區,也就是大約4GB的資料。這意味著一個檔案的每4GB空間對應一個GAM頁面。

   共享全域性分配對映(Shared Global Allocation Map,SGAM)頁面 這些頁面記錄了哪些區當前被用作混合型別的區,並且這些區需含有至少一個未使用的頁面。就像一個GAM頁面,每一個SGAM頁面覆蓋了大約64 000個區,也就是大約4GB的資料。一個SGAM頁面在它所覆蓋空間裡針對每一個區都有一個資料位。如果資料位為1,那麼對應的被使用的區為混合型別, 並且該區有一些自由頁面;如果資料位為0,那麼對應的區不是一個混合型別的區,或者雖然是一個混合型別的區,但是所有的頁面都已被使用了。

  表4-2顯示了基於每一個區當前的使用情況,在GAM和SGAM中該區所對應的位元位模式。

區的當前使用情況GAM位元位設定SGAM位元位設定
自由,未使用10
統一型別或已全部使用的混合區00
含有自由頁面的混合區01

   如果SQL Server需要找到一個新的完全沒有使用的區,那麼它可以使用任何一個在GAM頁面中對應的位元位值為1的區。如果SQL Server需要找到一個有著可用空間(有一個或多個自由頁面)的混合型別的區,那麼它可以尋找一個對應的GAM中的值為0、SGAM中的值為1的區。如 果不存在有可用空間的混合型別的區,SQL Server會使用GAM頁面來尋找一個全新的區並將其分配為混合型別的區,然後使用該區中的一頁。如果根本沒有自由區,那麼這個檔案已經滿了。

第0頁第1頁第2頁第3頁第4頁第5頁第6頁第7頁
m_type=15m_type=11m_type=8m_type=9m_type=0m_type=0m_type=16m_type=17
標頭檔案頁PFS頁GAM頁SGAM頁保留頁保留頁DCM頁BCM頁

   SQL Server能夠迅速地鎖定一個檔案中的GAM頁面,因為它總是位於任何資料庫檔案的第三頁上(頁碼為2)。SGAM頁面是在第四頁上(頁碼為3)。下一 個GAM頁面出現在第一個GAM頁面(頁碼為2)以後的每511 230個頁面中,並且下一個SGAM頁面出現在第一個SGAM頁面(頁碼為3)以後的每511 230個頁面中。每一個資料庫檔案的頁碼為0的頁面是檔案頭頁面,並且每個檔案僅有一頁。頁碼0是標頭檔案頁,頁碼1是頁面自由空間頁(Page Free Space,PFS)。

  在SQLServer2008的每一個資料庫中的前八頁順序都是固定的。

  除了第9頁為資料庫的BOOT頁以外,從第8頁到第173頁為SQLServer2008內部系統表的相關儲存資訊,然後從第174頁到第279頁為未分配頁面。因為第一頁從0開始,所以剛好280頁,即和我們看到的資料庫資料檔案的大小完全相等。

第8頁第8頁第8頁第N頁第173頁第279頁
m_type=1m_type=13m_type in (1,2,10)N/A
Data頁Boot頁主要為內部系統表相關資訊未分配

  以下截圖是通過SQLServer2008的Internals Viewer外掛看到的整體頁面結構,該外掛是從http://www.SQLInernalsViewer.com網站下載的,分為不同的.net版本。

  備註:TESTDB為新建立的空資料庫,沒有任何使用者自定義物件,直到有建表指令碼為止;

1

 關於資料庫頁型別如下所示:

型別頁面型別名稱頁面型別描述
1Data page堆表和聚集索引的葉子節點資料
2Index page聚集索引的非葉子節點和非聚集索引的所有索引記錄
3Text mixed pageA text page that holds small chunks of LOB values plus internal parts of text tree. These can be shared between LOB values in the same partition of an index or heap.
4Text tree pageA text page that holds large chunks of LOB values from a single column value.
7Sort page排序時所用到的臨時頁,排序中間操作儲存資料用的。
8GAM page全域性分配對映(Global Allocation Map,GAM)頁面 這些頁面記錄了哪些區已經被分配並用作何種用途。
9SGAM page共享全域性分配對映(Shared Global Allocation Map,GAM)頁面 這些頁面記錄了哪些區當前被用作混合型別的區,並且這些區需含有至少一個未使用的頁面。
10IAM page.有關每個分配單元中表或索引所使用的區的資訊
11PFS page.有關頁分配和頁的可用空間的資訊
13boot page.記錄了關於資料庫的資訊,僅存於每個資料庫的第9頁
15file header page記錄了關於資料庫檔案的資訊,存於每個資料庫檔案的第0頁
16DCM page記錄自從上次全備以來的資料改變的頁面,以備差異備份
17BCM page有關每個分配單元中自最後一條 BACKUP LOG 語句之後的大容量操作所修改的區的資訊

  實際上SQLServer還包括一些未公開的頁面型別,例如type 19,type 14等等。

  本章我們主要介紹GAM頁和SGAM頁,其他頁面型別會稍後介紹。

  那麼如何檢視頁面資訊呢,從SQLServer2000起便開始提供了一個讀取資料頁結構的命令DBCC Page。該命令為非文件化的命令,具體如下:

  DBCC Page ({dbid|dbname},filenum,pagenum[,printopt])

  具體引數描述如下:

  dbid               包含頁面的資料庫ID

  dbname       包含頁面的資料庫的名稱

  filenum         包含頁面的檔案編號

  pagenum      檔案內的頁面

  printopt            可選的輸出選項;選用其中一個值:

                            0:預設值,輸出緩衝區的標題和頁面標題

                           1:輸出緩衝區的標題、頁面標題(分別輸出每一行),以及行偏移量表

                           2:輸出緩衝區的標題、頁面標題(整體輸出頁面),以及行偏移量表

                           3:輸出緩衝區的標題、頁面標題(分別輸出每一行),以及行偏移量表;每一行後跟分別列出的它的列值

  如果要想看到這些輸出的結果,還需要設定DBCC TRACEON(3604)。

  如前文所述,GAM頁一定存在於該資料庫的第二個頁面,SGAM頁則一定存在於該資料庫的第三個頁面;而每一個資料庫都會存在檔案編號為1的資料庫檔案,所以我們執行以下命令即可。

#div_code img { border: 0px none; }
DBCC TRACEON(3604)
DBCC PAGE(TESTDB,
1,2,1)  —檢視GAM頁資訊
DBCC PAGE(TESTDB,
1,3,1)  —檢視SGAM頁資訊
DBCC PAGE(TESTDB,
1,2,2)  —檢視GAM頁資訊和整體輸出頁面
DBCC PAGE(TESTDB,
1,3,2)  —檢視SGAM頁資訊和整體輸出頁面
DBCC PAGE(TESTDB,
1,2,3)  —檢視GAM頁資訊及相應列值
DBCC PAGE(TESTDB,
1,3,3)  —檢視SGAM頁資訊及相應列值
DBCC PAGE(TESTDB,
1,2,1) WITH TABLERESULTS  —以表格形式檢視SGAM頁資訊及相應列值
DBCC PAGE(TESTDB,
1,3,1) WITH TABLERESULTS  —以表格形式檢視SGAM頁資訊及相應列值

 

  我們可以看到一個完整的頁面分為四個部分;BUFFER、PAGE HEADER、DATA和OFFSET TABLE。

  讓我們首先從GAM頁開始看起:

  BUFFER部分:

  顯示給定頁面的緩衝資訊,是記憶體中的結構,用於管理頁面,該資訊僅當該頁面處於記憶體時才有意義。關於這個部分我們知之甚少,基本上無法找到相關材料。

BUF @0x03585CD8每一次清空快取再次查詢,地址都會改變
bpage = 0x060B4000每一次清空快取再次查詢,地址都會改變
bhash = 0x00000000相對不變
bpageno = (1:2)當前頁面地址
bdbid = 8sys.databases.database_id
breferences = 1每一次清空快取再次查詢,地址都會改變
bUse1 = 41490每一次清空快取再次查詢,地址都會改變
bstat = 0xc00009相對不變
blog = 0x59ca2159相對不變
bnext = 0x00000000相對不變
   PAGE HEADER部分:

  PAGE HEADER部分顯示的是該頁面上的所有報頭欄位的資料

3
 

  PAGE HEADER這部分內容只有通過DBCC PAGE(TESTDB,1,2,2)即整體輸出頁面才能夠展現;通過與上面表格的對照,我們勉強能識別一些相關儲存資訊;當這部分缺乏官方文件的支援,為了避免無謂的猜測,所以暫時就不做深入探討了。

3
 

  DATA 部分

3
 

   DATA部分一般分為若干插槽號(Slot),如果是資料頁或索引頁的話,可以理解為一行記錄,SQLServer通過檔案號+頁面號+插槽號用來唯一標識表中的每一條記錄。但在GAM頁中我們可以把Slot 0理解為GAM頁的保留頁,共計94個位元組。

  從第194個位元組開始(頁面總是從第0個位元組開始的),到第196個位元組,這三個位元組代表已分配的分割槽的情況。即0000C0。

  我們再來看一下DBCC PAGE(TESTDB,1,2,3)的執行結果。

3
 

  上面顯示從第1頁到第168頁已分配,而第176頁到272頁未分配,和DBCC PAGE(TESTDB,1,2,2)顯示的194個頁面似乎有些矛盾,實際上是不矛盾的。如前文所述,GAM對未使用的分割槽標識為0,而對已分配的分割槽標識為1

  1個分割槽=64頁,因為前128個頁面均已分配,所以前兩個位元組為00 00

  從第128個頁面起到第175個頁面也均已分配,實際上為6個區為0也就是說連續6個bit為0,一個位元組為8個bit,最後兩個bit為11,所以該位元組為0000 0011,在此需要反轉一下相關二進位制位;反轉之後為1100 0000即為C0。

  最後讓我們用Internals Viewer外掛看一下GAM頁的全貌吧。

3
 SGAM頁面

#div_code img { border: 0px none; }
PAGE: (1:3)

BUFFER:
BUF @0x0358A7F4
bpage
= 0x062AE000             bhash = 0x00000000              bpageno = (1:3)
bdbid
= 8                      breferences = 3                       bUse1 = 14428
bstat
= 0xc00009               blog = 0x21212159              bnext = 0x00000000
                                                              
PAGE HEADER:                                                  
Page @0x062AE000                                              
m_pageId
= (1:3)               m_headerVersion = 1             m_type = 9
m_typeFlagBits
= 0x0           m_level = 0                     m_flagBits = 0x200
m_objId (AllocUnitId.idObj)
=99 m_indexId (AllocUnitId.idInd)=0 Metadata: AllocUnitId=6488064
Metadata: PartitionId
= 0      Metadata: IndexId = 0           Metadata: ObjectId = 99
m_prevPage
= (0:0)             m_nextPage = (0:0)              pminlen = 90
m_slotCnt
= 2                  m_freeCnt = 6                   m_freeData = 8182
m_reservedCnt
= 0              m_lsn = (18:435:5)              m_xactReserved = 0
m_xdesId
= (0:0)               m_ghostRecCnt = 0               m_tornBits = 177043542
Allocation Status              
GAM (
1:2)=ALLOCATED            SGAM (1:3)=NOT ALLOCATED          PFS(1:1)=0x44 ALLOCATED 100_PCT_FULL
DIFF (
1:6) = CHANGED           ML (1:7) = NOT MIN_LOGGED        

DATA:
Slot
0, Offset 0x60, Length 94, DumpStyle. BYTE
Record Type
= PRIMARY_RECORD         Record Attributes =                  
Memory Dump @0x4F32C060
00000000:   00005e00 00000000 00000000 00000000 ?..^.............        
00000010:   00000000 00000000 00000000 00000000 ?................        
00000020:   00000000 00000000 00000000 00000000 ?................        
00000030:   00000000 00000000 00000000 00000000 ?................        
00000040:   00000000 00000000 00000000 00000000 ?................        
00000050:   00000000 00000000 00000000 0000??????..............          

Slot
1, Offset 0xbe, Length 7992, DumpStyle. BYTE
Record Type
= PRIMARY_RECORD         Record Attributes =                  
Memory Dump @0x4F32C0BE
00000000:   0000381f 20ee2000 00000000 00000000 ?..8. . .........        
00000010:   00000000 00000000 00000000 00000000 ?................
00001F30:  
00000000 00000000 ???????????????????........          

 
   以下為DBCC PAGE(TESTDB,1,3,3)得到的相關資訊,有興趣的可以和20ee20做一下對比。

#div_code img { border: 0px none; }
(1:0)        - (1:32)       = NOT ALLOCATED                              
(
1:40)       -              =     ALLOCATED                              
(
1:48)       - (1:64)       = NOT ALLOCATED                              
(
1:72)       - (1:88)       =     ALLOCATED                              
(
1:96)       -              = NOT ALLOCATED                              
(
1:104)      - (1:120)      =     ALLOCATED                              
(
1:128)      - (1:160)      = NOT ALLOCATED                              
(
1:168)      -              =     ALLOCATED                              
(
1:176)      - (1:272)      = NOT ALLOCATED

 
  最後讓我們用Internals Viewer外掛看一下SGAM頁的全貌吧。

1
 

  總結一下,關於GAM和SGAM頁比較困難的地方:

  1、 關於GAM和SGAM頁中的BUFFER資訊基本無法理解,也找不到相關材料。

  2、 PAGE HEADER的部分資訊和Slot 0中的一部分資訊,也無法找到相關材料。

  3、 SGAM頁中的NOT ALLOCATED實際上是統一型別區或者已使用完的混合型別的區,而ALLOCATED實際上為含有自由頁面的混合區。

  4、 GAM頁中0代表已分配,1代表自由區;和一般的標誌位的含義剛好相反。

  5、 GAM和SGAM實際上只分配了280個頁面,即35個區;顯示出來的資料內容雖然很多,但後面的分割槽資訊實際上是不存在的。

  6、 GAM和SGAM通過DBCC的printopt為3的屬性顯示出來的頁面分配資訊看似是斷號的。

  7、 GAM和SGAM的區資訊的位元組是通過二級制反轉得到的。

   GAM和SGAM頁的總的大小為8192個位元組;檔案頭為96個位元組,slot 0為94個位元組,slot 1的頭部的系統資訊為4個位元組,尾部的系統資訊為10個位元組,所以有效儲存應為7988個位元組,63904個區,511230個頁;事實上當資料檔案超過 約4G的時候,我們將能在第511232頁、 第511233頁分別找到其對應的GAM、SGAM頁面。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/6517/viewspace-676867/,如需轉載,請註明出處,否則將追究法律責任。

相關文章