如何深入分析SQL Server Page结构

63次阅读
没有评论

共计 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 结构就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-17发表,共计4435字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)