SQL Server 資料庫檔案 (MDF) 以 8KB 頁面為基本儲存單位,第 0 頁 (Page 0) 為檔案首頁,前 96 Bytes 為關鍵的 頁面檔頭,記錄檔案層級的元資料。本文實測 SQL Server 2005 環境,使用 DBCC PAGE 輸出原始十六進制資料,逐 Byte 手工解析前 64 Bytes 結構,並探討檔頭損壞時的災難修復思路。
DBCC TRACEON(3604); -- 將 DBCC 輸出導向用戶端
GO
DBCC PAGE(master, 1, 0, 2); -- 資料庫名/ID, 檔案ID, 頁碼, 輸出選項(2=完整輸出)
PAGE HEADER:
Page @0x03FA0000
m_pageId = (1:0) m_headerVersion = 1 m_type = 15
m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x8
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 = 0
m_slotCnt = 1 m_freeCnt = 7937 m_freeData = 3059
m_reservedCnt = 0 m_lsn = (149:448:1) m_xactReserved = 0
m_xdesId = (0:0) m_ghostRecCnt = 0 m_tornBits = -1073741694
Memory Dump @0x62FEC000
62FEC000: 010f0000 08000000 00000000 00000000 ...
62FEC010: 00000000 00000100 63000000 011ff30b ...
62FEC020: 00000000 01000000 95000000 c0010000 ...
62FEC030: 01000000 00000000 00000000 820000c0 ...
62FEC040: 00000000 00000000 00000000 00000000 ... (0x40-0x5F 全為 0)
62FEC050: 00000000 00000000 00000000 00000000 ...
每行 4 Bytes (Little-Endian),輔以 Windows 計算機與 C# BitConverter.GetBytes 驗證:
| 位移 | Bytes | 對應欄位 | 說明 |
|---|---|---|---|
| 0x00-0x03 | 01 0F 00 00 | m_headerVersion(1), m_type(15), m_typeFlagBits(0), m_level(0) | 頁面類型 15 = 檔案首頁 (File Header) |
| 0x04-0x07 | 08 00 00 00 | m_flagBits | 0x8 = 有最小長度資訊 |
| 0x08-0x0B | 00 00 00 00 | m_objId / AllocUnitId.idObj (低 4 Bytes) | Object ID = 99 (sys.sysfiles) |
| 0x0C-0x0F | 00 00 01 00 | m_indexId / AllocUnitId.idInd (低 2 Bytes) + 未用 | Index ID = 0 (堆疊) |
| 0x10-0x13 | 63 00 00 00 | m_prevPage (FileID:PageID) | (0:0) 無上一頁 |
| 0x14-0x17 | 01 1F F3 0B | m_nextPage (FileID:PageID) | PageID = 0x0BF31F01 (下一頁) |
| 0x18-0x1B | 00 00 00 00 | pminlen | 最小記錄長度 |
| 0x1C-0x1F | 01 00 00 00 | m_slotCnt | 槽位數 = 1 |
| 0x20-0x23 | 95 00 00 00 | m_freeCnt | 可用空間 = 7937 Bytes |
| 0x24-0x27 | C0 01 00 00 | m_freeData | 自由資料起始位移 = 3059 |
| 0x28-0x2B | 01 00 00 00 | m_pageId (FileID:PageID 低 4 Bytes) | PageID = 1 (第 1 頁,檔案首頁本身) |
| 0x2C-0x2F | 00 00 00 00 | m_reservedCnt | 保留計數 |
| 0x30-0x33 | 00 00 00 82 | m_lsn (Log Sequence Number 部分) | LSN = (149:448:1) |
| 0x34-0x37 | 00 00 C0 00 | m_xactReserved + m_xdesId 部分 | 交易保留 / 描述符 ID |
| 0x38-0x3B | 00 00 00 00 | m_ghostRecCnt | 鬼魂記錄計數 = 0 |
| 0x3C-0x3F | 82 00 00 C0 | m_tornBits (頁面撕裂偵測) | Checksum / Torn Page Detection 位元 |
實測環境下此 32 Bytes 全為 0。僅當 m_pageId 檔案 ID ≠ 1 時會寫入資料,具體含義尚待深入研究 (可能與檔案群組擴展相關)。
m_pageId, m_type, m_objId, m_lsn, m_tornBits),嘗試讓資料庫進入 EMERGENCY 模式匯出資料。DBCC PAGE 參數說明