当前位置: 移动技术网 > IT编程>数据库>MSSQL > SQL Server Page结构深入分析

SQL Server Page结构深入分析

2017年12月08日  | 移动技术网IT编程  | 我要评论

佻褐朱澧,81813,jeesan

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 @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;

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网