SQL Server数据页的结构大体包括三个部分,即标头、数据行和行偏移量。
现在让我们正式进入数据页面去看一下数据页面的构造,让我们首先去访问一下该表的数据首页即第224个页面。
Dbcc page(testdb,1,224,2)
PAGE HEADER部分,即该页面的前96个字节。
m_pageId = (1:224) | 当前页面号码 |
m_headerVersion = 1 | 版本号,始终为1 |
m_type = 1 | 当前页面类型,m_type=1表示数据页面 |
m_typeFlagBits = 0x4 | 数据页和索引页为4,其他页为0 |
m_level = 0 | 该页在索引页(B树)中的级数,0表示为叶子节点 |
m_flagBits = 0x8200 | 页面标志 |
m_objId (AllocUnitId.idObj) = 94 | |
m_indexId (AllocUnitId.idInd) = 256 | |
Metadata: AllocUnitId = 72057594044088320 | 存储单元的ID,sys.allocation_units.allocation_unit_id |
Metadata: PartitionId = 72057594039107584 | 数据页所在的分区号,sys.partitions.partition_id |
Metadata: IndexId = 0 | 对象的索引号,sys.objects.object_id&sys.indexes.index_id |
Metadata: ObjectId = 133575514 | 该页面所属的对象的id,sys.objects.object_id |
m_prevPage = (0:0) | 该数据页的前一页面 |
m_nextPage = (0:0) | 该数据页的后一页面 |
pminlen = 108 | 定长数据所占的字节数为108个字节 ID INT IDENTITY(1,1) NOT NULL, type CHAR(100) NOT NULL, 共计104个字节,每个定长字段需要2个字节的管理字节 |
m_slotCnt = 62 | 页面中的数据的行数,每页62条记录 |
m_freeCnt = 293 | 页面中剩余的空间,还剩293字节的空间 |
m_freeData = 7775 | 从第一个字节到最后一个字节的空间字节数(包括96字节的文件头的长度) |
m_reservedCnt = 0 | 活动事务释放的字节数 |
m_lsn = (67:272:3) | 日志记录号 |
m_xactReserved = 0 | 最新加入到m_reservedCnt领域的字节数 |
m_xdesId = (0:0) | 添加到m_reservedCnt的最近的事务id |
m_ghostRecCnt = 0 | 幻影数据的行数 |
m_tornBits = 1213019927 | 页的校验位或者被由数据库页面保护形式决定分页保护位取代 |
上在页的尾部还有个行偏移矩阵,记录了每条记录的起始位置,每条记录需要2个字节来记录该位置,所以62条记录共计124个维护字节,加上293个剩余空间和实际已使用的7775个字节,刚好8192个字节,即一页。
从Offset table和page结构可以知道,第一条记录从第96个字节开始。
ID | name | Type | other | describle |
1 | name1 | 1111111111111111111111111111111111. | 8 | NULL |
如前文所说,关于数据的存储从第96个字节开始
关于数据行的结构我们还可以采用稍微宏观一些的视角来查看。
其中状态A为如下说明:
bit0:版本信息,在SQL Server 2005/08总是为0
bit1-3: 0=(primary record);1=(forwarded record);2=(forwarding stud);3=(index record);4=(溢出数据);5=(ghost索引记录);6=(ghost数据记录)
bit4:表示存在NULL位图(在数据行里SQL2005/08总存在NULL位图)
bit5:表示存在变长列
bit6:未启用
bit7:表示存在幽灵记录
本例中30->00110000 它是一个行属性的位图 从高位存到地位(右边第一位是bit0),bit4为1即存在变长列的字段,因为在SQLServer2005/2008中总存在NULL位图,所以bit5也为1。
状态位B在SQLServer2005//2008中未启用,所以为00
记录定长部分的长度为2个字节,是所有定长字段的长度之和加4,该处为int类型4个字节,char(100)为100个字节,再加上4,所以为108,换算成16进制即6c。
紧跟其后的为定长字段的内容,即ID字段的4个字节和TYPE字段的100个字节。
固定长度的字段数据之后,是该表的总字段数,用两个字节表示,本表包括5个字段所以为05 00。
NULL位图:f0->11110000 因为该表只有列 所以只需要看后面个,1表示该行的对应列为NULL或者该位图未使用。本表前4个字段不为空,第5个为空,第6-8未使用。
接下来是行内存储数据的变长列的数目:0200->00000000 00000010=2 表示该行存储了列name和other字段的数据。
第一变长列数据终止位置为:7a00->00000000 01111010=122=1+1+2+(4+100)+2+ceiling(5/8)+2+2+2+len(“name1”)
第二变长列数据终止位置:7b00->00000000 01111011=123 实际上就是在前者的基础上加了第二个变长列的字段长度。
1+1+2+(4+100)+2+ceiling(5/8)+2+2+2+len(“name1”)+len(“8”)
第一列变长列的数据: 6e616d 6531换算成字符即'name1'
第二列变长列的数据:38换算成字符即8