頁存儲基礎
- SQL Server 數據文件由連續 8KB 頁組成 (8192 字節)。
- 每頁 96 字節頁頭,數據區 + 頁尾槽位數組 (倒序寫入,每槽 2 字節)。
- 頁類型由頁頭
m_type 標識,可通過 DBCC PAGE 或 sys.dm_os_buffer_descriptors 查看。
14 種核心頁類型詳解
| 類型 | 名稱 | m_type | 功能描述 | 特殊位置規律 |
| 1 | Data Page (數據頁) | 1 | 堆表數據頁、聚集索引葉級頁 | 隨機分佈 |
| 2 | Index Page (索引頁) | 2 | 非聚集索引非葉級、聚集索引非葉級 | 隨機分佈 |
| 3 | Text Mixed Page (文本混合頁) | 3 | 短 LOB 數據,多類型多行混存 | 隨機分佈 |
| 4 | Text Tree Page (文本樹頁) | 4 | 單個大 LOB 值的樹結構 | 隨機分佈 |
| 5 | Sort Page (排序頁) | 19 | 排序操作臨時頁 (常見於 TempDB) | 隨機分佈 |
| 6 | GAM (全局分配映射頁) | 8 | 記錄非混合區分配狀態 (1=空閒, 0=已用) | 每文件第 2 頁 (PageID=2) 及其後每 511230 頁 |
| 7 | SGAM (共享全局分配映射頁) | 9 | 記錄混合區分配狀態 (1=有空閒, 0=已滿) | 每文件第 3 頁 (PageID=3) 及其後每 511230 頁 |
| 8 | IAM (索引分配映射頁) | 10 | 記錄特定對象 (堆/索引) 的區分配鏈 | 隨機分佈 |
| 9 | PFS (空閒空間跟蹤頁) | 11 | 跟蹤每頁填充度 (空/1-50%/51-80%/81-95%/96-100%) | 每文件第 1 頁 (PageID=1) 及其後每 8088 頁 |
| 10 | Boot Page (啟動頁) | 13 | 存儲數據庫級元數據 (版本、兼容級別、檢查點 LSN 等) | 文件 1 第 9 頁 (PageID=9) |
| 11 | Server Config Page (服務配置頁) | - | sys.configurations 部分信息 | master 文件 1 第 10 頁 |
| 12 | File Header Page (文件頭頁) | 15 | 文件屬性 (大小、增長、LSN、組 ID 等) | 每文件第 0 頁 (PageID=0) |
| 13 | DCM (差異更改映射頁) | 16 | 記錄上次全備/差異備份後更改的區 | 每文件第 6 頁 (PageID=6) 及其後每 511230 頁 |
| 14 | BCM (大容量更改映射頁) | 17 | 記錄上次日志備份後大容量操作更改的區 | 每文件第 7 頁 (PageID=7) 及其後每 511230 頁 |
緩存頁類型分布查詢
SELECT
CASE page_type
WHEN 'DATA_PAGE' THEN '數據頁'
WHEN 'INDEX_PAGE' THEN '索引頁'
WHEN 'TEXT_MIX_PAGE' THEN '文本混合頁'
WHEN 'TEXT_TREE_PAGE' THEN '文本樹頁'
WHEN 'IAM_PAGE' THEN 'IAM頁'
WHEN 'GAM_PAGE' THEN 'GAM頁'
WHEN 'SGAM_PAGE' THEN 'SGAM頁'
WHEN 'PFS_PAGE' THEN 'PFS頁'
WHEN 'FILEHEADER_PAGE' THEN '文件頭頁'
WHEN 'BOOT_PAGE' THEN '啟動頁'
WHEN 'DIFF_MAP_PAGE' THEN 'DCM頁'
WHEN 'ML_MAP_PAGE' THEN 'ML_MAP頁 (最小日志/BCM)'
WHEN 'BULK_OPERATION_PAGE' THEN 'BULK_OPERATION頁 (謎之類型)'
WHEN 'SORT_PAGE' THEN '排序頁'
ELSE page_type
END AS 頁類型中文,
page_type,
COUNT(*) AS 頁數
FROM sys.dm_os_buffer_descriptors
WHERE database_id = DB_ID()
GROUP BY page_type
ORDER BY 頁數 DESC;
神秘案例:BULK_OPERATION_PAGE (m_type=20) 是什麼?
現象
- 緩存視圖顯示
page_type = 'BULK_OPERATION_PAGE'。
- 按文檔,BCM 頁對應
ML_MAP_PAGE (m_type=17),首頁固定在 PageID=7。
- 但
BULK_OPERATION_PAGE 的頁 ID 連續 (如 100, 101, 102...),非固定 PageID=7。
DBCC PAGE 顯示其 m_type = 20,官方文檔未見記載。
探索過程
-- 1. 查詢緩存中的 BULK_OPERATION_PAGE
SELECT TOP 10 database_id, file_id, page_id, page_type
FROM sys.dm_os_buffer_descriptors
WHERE page_type = 'BULK_OPERATION_PAGE' AND database_id = DB_ID();
-- 2. 拿一個 page_id 做 DBCC PAGE
DBCC TRACEON(3604);
DBCC PAGE (YourDB, 1, 100, 3); -- 輸出顯示 m_type = 20
-- 3. 對比 ML_MAP_PAGE (理論上的 BCM)
SELECT TOP 10 database_id, file_id, page_id
FROM sys.dm_os_buffer_descriptors
WHERE page_type = 'ML_MAP_PAGE' AND database_id = DB_ID();
-- 發現 ML_MAP_PAGE 首頁確實在 PageID=7,DBCC PAGE 顯示 m_type=17
結論
- ML_MAP_PAGE (m_type=17) = BCM (大容量更改映射頁),固定位置,記錄最小日志操作更改區。
- BULK_OPERATION_PAGE (m_type=20):官方未公開文檔,推測為大容量操作 (BULK INSERT/BCP/SELECT INTO) 運行時的臨時工作頁,或大容量日志記錄的緩存頁,非常駐固定位置。
- UNLINKED_REORG_PAGE (若出現) 疑為 排序頁/重組臨時頁 (m_type=19)。
DBCC PAGE 實戰速查
-- 語法
DBCC TRACEON(3604); -- 輸出到客戶端
DBCC PAGE ( { 'dbname' | dbid }, filenum, pagenum [, printopt = 0|1|2|3 ] );
-- printopt:
-- 0: 只頁頭
-- 1: 頁頭 + 槽位數組 + 每行偏移
-- 2: 頁頭 + 數據區十六進制傾印
-- 3: 頁頭 + 完整行解析 (最詳細)
-- 例子:查看文件頭頁 (PageID=0)
DBCC PAGE (YourDB, 1, 0, 3);
-- 例子:查看某數據頁
DBCC PAGE (YourDB, 1, 150, 1);
頁面鏈結構圖解 (文本版)
+------------------+
| Page Header | 96 bytes (m_type, m_objId, m_prevPage, m_nextPage, m_slotCnt...)
+------------------+
| |
| Data Rows | 變長,從頁頭後向下增長
| (Slot 0, 1...) |
| |
+------------------+
| |
| Slot Array | 從頁尾向前增長,每槽 2 bytes,存儲行偏移量
| (倒序: 最大槽號在最前) |
| |
+------------------+
參考資源