SQL Server存儲數據的基本單元是Page,每一個Page的大小是8KB,數據文件是由Page構成的。在同一個數據庫上,每一個Page都有一個唯一的資源標識,標識符由三部分組成:db_id,file_id,page_id,例如,15:1:8733,15是數據庫的ID,1是數據文件的ID,8733是Page的編號,Page的編號從0依次遞增。8個連續的Page組成一個區(Extent),數據文件中已分配(Allocated)的空間被分割成區的整數倍。一次磁盤IO操作作用於Page級別,而空間分配的最小單元是區。
一,Page的類型
Page是用於存儲數據的,不同類型的Page存儲的數據是不同的,Page的結構也是不同的。有些Page是用於存儲數據的,叫做Data Page,有些Page是用於存儲索引結構中的中間節點的,叫做Index Page,有些Page是SQL Server存儲引擎使用的,用於管理Page的,叫做系統頁。Page類型和存儲的數據類型如下表所示:
| Page type | Contents |
|---|---|
| Data | Data rows with all data, except text, ntext, image, nvarchar(max), varchar(max), varbinary(max), and xml data, when text in row is set to ON. |
| Index | Index entries. |
| Text/Image | Large object data types: (text, ntext, image, nvarchar(max), varchar(max), varbinary(max), and xml data) Variable length columns when the data row exceeds 8 KB: (varchar, nvarchar, varbinary, and sql_variant) |
| Global Allocation Map, Shared Global Allocation Map | Information about whether extents are allocated. |
| Page Free Space (PFS) | Information about page allocation and free space available on pages. |
| Index Allocation Map | Information about extents used by a table or index per allocation unit. |
| Bulk Changed Map | Information about extents modified by bulk operations since the last BACKUP LOG statement per allocation unit. |
| Differential Changed Map | Information about extents that have changed since the last BACKUP DATABASE statement per allocation unit. |
日志文件沒有Page結構,它是由一系列的日志記錄構成的。本文關注的是Data Page和Index Page,跟數據表有關。
二,數據Page的結構
每一個Page都由頭部(Header),內容(Content)和行偏移量(Offset)組成,也叫做Slot,頭部是在Page的開始處,占用96Bytes,用於存儲Page的編號,Page的類型,分配單元(Allocation Unit)等系統信息。注:在單個Page中最多存儲8060Bytes的數據,也就是一行要想存儲到一個Page中,該行包含的字節最大不能超過8060Bytes,超過8060Bytes的行會被拆分。

數據行存儲在Page Header之后,數據行在Page中的物理存儲是無序的,行的邏輯順序是由行偏移(Row Offset)確定的,行偏移存儲在Page的末尾,每一個行偏移是一個Slot,占用2B。行偏移連續排列在Page的末尾,稱作槽數組(Slot Array)。行偏移以倒序方式存儲行的偏移量,這意味着,從Page末尾向Page 開頭計數,第一行的偏移量存儲在Page的末尾Slot中,第二行的偏移量存儲在Page末尾的第二個Slot中。
三,DBCC PAGE()命令
PAGE中存儲的數據頁或索引頁,可以使用非正式的命令來查看:
DBCC PAGE(['database name'|database id], file_id, page_number, print_option = [0|1|2|3] )
參數注釋:
- database_id:page所在的數據庫
- file_id:數據庫文件的ID;
- page_number:該Page在文件中的編號;
- print_option是指打印信息的詳細程度,默認值是0,只打印Page Header。
參數Print_option的有效值是0、1、2、3,表示顯示信息的詳細程度:
- 0 – print just the page header
- 1 – page header plus per-row hex dumps and a dump of the page slot array (unless its a page that doesn’t have one, like allocation bitmaps)
- 2 – page header plus whole page hex dump
- 3 – page header plus detailed per-row interpretation
四,查看Page頭部信息
Page頭部信息存儲的是Page的系統信息,例如,查看資源標識符:15:1:8777733 Page的頭部信息:
dbcc traceon(3604) dbcc page(15,1,8777733)
在我的數據庫中,該Page的頭部信息(移除Buffer的數據)如下所示,
PAGE: (1:8777733) PAGE HEADER: Page @0x0000005188B02000 m_pageId = (1:8777733) m_headerVersion = 1 m_type = 1 m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x220 m_objId (AllocUnitId.idObj) = 28503 m_indexId (AllocUnitId.idInd) = 256 Metadata: AllocUnitId = 72057595905900544 Metadata: PartitionId = 72057594059423744 Metadata: IndexId = 1 Metadata: ObjectId = 1029578706 m_prevPage = (1:8777732) m_nextPage = (1:8777734) pminlen = 16 m_slotCnt = 2 m_freeCnt = 4513 m_freeData = 3675 m_reservedCnt = 0 m_lsn = (1212327:16:558) m_xactReserved = 0 m_xdesId = (0:799026688) m_ghostRecCnt = 0 m_tornBits = -1518328013 DB Frag ID = 1 Allocation Status GAM (1:8690944) = ALLOCATED SGAM (1:8690945) = NOT ALLOCATED PFS (1:8775480) = 0x40 ALLOCATED 0_PCT_FULL DIFF (1:8690950) = CHANGED ML (1:8690951) = NOT MIN_LOGGED
Page 頭部返回的各個字段的含義:
1,Page的編號
m_pageId = (1:8777733),該Page所在的File ID 和Page ID
2,Page的類型
m_type = 1,Page的類型,常見的類型是數據頁和索引頁:
- 1 – data page,用於表示:堆表或聚集索引的葉子節點
- 2 – index page,用於表示:聚集索引的中間節點或者非聚集索引中所有級別的節點
其他Page類型(系統頁是管理Page的Page,例如,GAM,IAM等)如下:
- 3 – text mix page,4 – text tree page,用於存儲類型為文本的大對象數據
- 7 – sort page,用於存儲排序操作的中間數據結果
- 8 – GAM page,用於存儲全局分配映射數據GAM(Global Allocation Map),每一個數據文件被分割成4GB的空間塊(Chunk),每一個Chunk都對應一個GAM數據頁,GAM數據頁出現在數據文件特定的位置處,一個bit映射當前Chunk中的一個區。
- 9 – SGAM page,用於存儲SGAM頁(Shared GAM)
- 10 – IAM page,用於存儲IAM頁(Index Allocation Map)
- 11 – PFS page,用於存儲PFS頁(Page Free Space)
- 13 – boot page,用於存儲數據庫的信息,只有一個Page,Page的標識符是:db_id:1:9,
- 15 – file header page,存儲數據文件的數據,數據庫的每一個文件都有一個,Page的編號是0。
- 16 – diff map page,存儲差異備份的映射,表示從上一次完整備份之后,該區的數據是否修改過。
- 17 – ML map page,表示從上一次備份之后,在大容量日志(bulk-Logged)操作期間,該區的數據是否被修改過,This is what allows you to switch to bulk-logged mode for bulk-loads and index rebuilds without worrying about breaking a backup chain.
- 18 – a page that’s be deallocated by DBCC CHECKDB during a repair operation.
- 19 – the temporary page that ALTER INDEX … REORGANIZE (or DBCC INDEXDEFRAG) uses when working on an index.
- 20 – a page pre-allocated as part of a bulk load operation, which will eventually be formatted as a ‘real’ page.
3,Page在索引中的級數
數據頁在索引中的索引級數,m_level=0,表示處於Leaf Level。
- 對於堆表(Heap),m_level=0表示的是Data Page;
- 對於聚集索引,m_level=0表示的是Data Page;
- 對於非聚集索引,m_level=0表示的是葉子節點
4, Page的元數據
Page的元數據十分重要,不僅能夠查看處Page所在的Object,甚至能夠查看該Page所在的分配單元和分區ID,在死鎖進行故障排除時十分有用
- Metadata: AllocUnitId =72057595905900544,該Page所在的分配單元ID(allocation_unit_id)
- Metadata: PartitionId =72057594059423744,該Page所在的分區的分區ID(partition_id)
- Metadata: IndexId = 1,該Page所在的索引ID
- Metadata: ObjectId = 1029578706,用於表示Page所屬對象的object_id
5,page的鏈指針
由於數據表的Page並不是單獨存在的,而是通過雙向鏈式結構連接在一起的,
- m_prevPage = (1:8777732) :用於表示前一個page (FileID : PageID)
- m_nextPage = (1:8777734) :用於表示下一個page (FileID:PageID)
6, 其他頭部字段
- m_slotCnt = 2 :頁面中Slot的數量,用於Page中存儲的數據行數
- m_freeCnt = 4513 :頁面中剩余的空間,單位是字節,還剩83字節的空間
- m_reservedCnt = 0 :為活動事務保留的存儲空間,單位是字節
- m_ghostRecCnt = 0 :頁面中存在的幽靈記錄的總數(ghost record count)
關於Page頭部的信息,可以閱讀《Inside the Storage Engine: Anatomy of a page》;
五,利用Page的元數據排除死鎖
Page的元數據包含分區ID,索引ID和對象ID,用戶可以使用這些元數據,分析死鎖產生的原因。系統追蹤到產生死鎖的資源,可能是一個Page的資源標識符,如果能夠確認發生死鎖是由於數據表或索引的分區不合理導致的,那么可以重新設置分區列,或者設置分區邊界值,把單個分區拆分成多個分區,這樣就能把競爭的臨界資源分配到不同的分區中,避免查詢請求對資源的競爭,進而減少死鎖的發生。
- Metadata: PartitionId ,該Page所在的分區的分區ID(partition_id);
- Metadata: IndexId ,該Page所在索引ID;
- Metadata: ObjectId,用於表示對象的object_id;
參考文檔:
Pages and Extents Architecture Guide
Using DBCC PAGE to Examine SQL Server Table and Index Data
Inside the Storage Engine: How are allocation unit IDs calculated?
Inside the Storage Engine: Anatomy of a page
筆記17 DBCC IND()非常詳細解釋加dbcc page([GPOSDB],1,119,3)非常詳細解釋 2013-1-20
