共計 4435 個字符,預計需要花費 12 分鐘才能閱讀完成。
這篇文章將為大家詳細講解有關如何深入分析 SQL Server Page 結構,文章內容質量較高,因此丸趣 TV 小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
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 是用于存儲數據的,叫做 Data Page,有些 Page 是用于存儲索引結構中的中間節點的,叫做 Index Page,有些 Page 是 SQL Server 存儲引擎使用的,用于管理 Page 的,叫做系統頁。本文關注的是 Data Page 和 Index Page,跟數據表有關。
日志文件沒有 Page 結構,它是由一系列的日志記錄構成的。
一,Page 的結構
每一個 Page 都由 頭部(Header),內容(Content)和行偏移量(Offset)組成,頭部是在 Page 的開始處,占用 96Bytes,用于存儲 Page 的編號,Page 的類型,分配單元(Allocation Unit)等系統信息。注:在單個 Page 中最多存儲 8060Bytes 的數據。
The maximum amount of data and overhead that is contained in a single row on a page is 8,060 bytes (8 KB).
數據行存儲在 Page Header 之后,數據行在 Page 中的物理存儲是無序的,行的邏輯順序是由行偏移(Row Offset)確定的,行偏移存儲在 Page 的末尾,每一個行偏移是一個 Slot,占用 2B。行偏移連續排列在 Page 的末尾,稱作槽數組(Slot Array)。行偏移以倒序方式存儲行的偏移量,這意味著,從 Page 末尾向 Page 開頭計數,第一行的偏移量存儲在 Page 的末尾 Slot 中,第二行的偏移量存儲在 Page 末尾的第二個 Slot 中。
二,查看 Page 頭部信息
Page 頭部信息存儲的是 Page 的系統信息,可以使用非正式的命令來查看:
DBCC PAGE([database name |database id], file_id, page_number, print_option = [0|1|2|3] )
參數:file_id 是數據庫文件的 ID;page_number 是 Page 在當前文件中的編號;print_option 是指打印信息的詳細程度,默認值是 0,只打印 Page Header。
例如,查看資源標識符:15:1:8777733 Page 的頭部信息:
dbcc traceon(3604)dbcc page(15,1,8777733)
在我的數據庫中,該 Page 的頭部信息(移除 Buffer 的數據)如下所示,
PAGE: (1:8777733)PAGE HEADER:Page @0x0000005188B02000m_pageId = (1:8777733) m_headerVersion = 1 m_type = 1m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x220m_objId (AllocUnitId.idObj) = 28503 m_indexId (AllocUnitId.idInd) = 256 Metadata: AllocUnitId = 72057595905900544 Metadata: PartitionId = 72057594059423744 Metadata: IndexId = 1Metadata: ObjectId = 1029578706 m_prevPage = (1:8777732) m_nextPage = (1:8777734)pminlen = 16 m_slotCnt = 2 m_freeCnt = 4513m_freeData = 3675 m_reservedCnt = 0 m_lsn = (1212327:16:558)m_xactReserved = 0 m_xdesId = (0:799026688) m_ghostRecCnt = 0m_tornBits = -1518328013 DB Frag ID = 1 Allocation StatusGAM (1:8690944) = ALLOCATED SGAM (1:8690945) = NOT ALLOCATED PFS (1:8775480) = 0x40 ALLOCATED 0_PCT_FULL DIFF (1:8690950) = CHANGEDML (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 所在的索引 IDMetadata: ObjectId = 1029578706,用于表示 Page 所屬對象的 object_id5,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;
關于如何深入分析 SQL Server Page 結構就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。