微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

剖析SQLSERVER2005的页面结构--行类型篇一

剖析sqlSERVER2005页面结构

行类型篇一

 

 

(和我平常说的表的记录行有点差别哦,注意理解)简单的可理解为数据库物理存储的表、索引及其它类型的数据的基本单元。当然行结构远不止如下说明的那么简单,更深入的有待再度剖析,比如打开行版本后slot记录就会多出14个字节的xsn

行记录是表里的数据,记录着表的内容。反之也就是表的记录是以数据行的形式保存在数据库页面上。它一般是堆表的行或索引的叶级行,显然的是它一定保存着行的所有列数据,在这儿我们得注意一下,这个所有的列包含两层意思,一就是列的数据,另外可能是行溢出数据的指针。指针的情况又分为两种可能:一是使用了LOB数据类型,另一个是非LOB类型存储在超行最大8060限制时。这些不在此篇讨论的范围,一带而过。

关于如何查看页面内容方法我在 SQL2005中的事务与锁定(九)一文有详细的说明,下面为了便于说明先写一个测试用例:

 

create table testtb(id int identity(1,1),col char(985))

go

insert into testtb

select REPLICATE('a',5) union all

select REPLICATE('b',5) union all

select REPLICATE('c',5) union all

select REPLICATE('d',5) union all

select REPLICATE('e',5) union all

select REPLICATE('f',5) union all

select REPLICATE('g',5) union all

select REPLICATE('h',5) union all

select REPLICATE('i',985) union all

select REPLICATE('j',985)

 

go

 

 

alter table testtb

alter column col char(1000) --

go

dbcc ind(testcsdn,testtb,-1)--220 221 232

dbcc traceon(3604)

dbcc page(testcsdn,1,220,1)

@H_102_404@/*

@H_102_404@DATA:

@H_102_404@Slot 0,Offset 0x60,Length 9,DumpStyle BYTE

Record Type = FORWARDING_STUB@H_102_404@        Record Attributes =            

@H_102_404@Memory Dump @0x4431C060

@H_102_404@0000000:   04dd0000 00010002 00†††††††††††††††††.........      

@H_102_404

@H_102_404@Slot 1,Offset 0x69,Length 1996,DumpStyle BYTE

Record Type = PRIMARY_RECORD@H_102_404@         Record Attributes =  NULL_BITMAP

@H_102_404@Memory Dump @0x4431C069

@H_102_404@......

@H_102_404

@H_102_404@Slot 2,Offset 0x835,DumpStyle BYTE

Record Type = FORWARDING_STUB @H_102_404@       Record Attributes =      

@H_102_404@Memory Dump @0x4431C835

@H_102_404@......                        

@H_102_404@000007C0:   20202020 20202020 200300f8 ††††††††††         ...    

@H_102_404

@H_102_404@DBCC 执行完毕。如果@H_102_404@DBCC 输出错误信息,请与系统管理员联系。@H_102_404@

@H_102_404@*/

dbcc page(testcsdn,221,1)

@H_102_404@/*

@H_102_404@PAGE: (1:221)

@H_102_404@DATA:

@H_102_404@Slot 0,Offset 0x1014,DumpStyle BYTE

Record Type = PRIMARY_RECORD @H_102_404@        Record Attributes =  NULL_BITMAP

@H_102_404@Memory Dump @0x3432D014

@H_102_404@00000000:   1000c907 09000000 69696969 69696969 †.......iiiiiiii 

@H_102_404@......

@H_102_404@......

@H_102_404@Slot 2,Length 2010,DumpStyle BYTE

Record Type = FORWARDED_RECORD@H_102_404@       Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

@H_102_404@Memory Dump @0x3432C060

@H_102_404@00000000:   3200c907 01000000 61616161 61202020 †.......aaaaa   

@H_102_404@*/

 

 

上例只是出现了行类型的一部分(PRIMARY_RECORDFORWARDING_STUB FORWARDED_RECORD),下面我们来说说行的类型有哪几类:

 

1PRIMARY_RECORD 主记录

Record Type = PRIMARY_RECORD时表明当前记录是主记录,我们随便找一个主记录行前四个字节:@H_102_404@1000c907 状态位A为:0x10,bit1-3位全是0 ,当全是0时代表当前行是主记录。行的基本结构我在SQL2005中的事务与锁定(九)一文有详细介绍,在这儿不再细说。

 

Record Type

行前四个字节

状态位A

bit1-3

PRIMARY_RECORD

@H_102_404@1000c907

0x10

0

 

2、FORWARDING_STUB 转移根

       Record Type = FORWARDING_STUB时表明当前记录是行转移后留下的一条转移根信息。在这儿我们应该清楚一点这只会发生在堆表,因为在当前位置已经无法保存数据,所以搬移当前位置,只是在当前位置留一个类指针的数据,指向最终搬向的数据。从技术上来说,它也避免sqlSERVER误把数据存在原来的位置,但是也增加了select的IO量,虽然我们可用使用聚集索引来回避,但是这种性能上的得失全是在于DBA自己权衡。请看如下页面信息片断:

 

@H_102_404@Slot 0,DumpStyle BYTE

Record Type = FORWARDING_STUB@H_102_404@        Record Attributes =                  

@H_102_404@Memory Dump @0x4431C060

@H_102_404@0000000:   04dd0000 00010002 00†††††††††††††††††.........

       我们发现这一行只有9个字节的长度,细一看和我在SQL2005中的事务与锁定(九)介绍的基本行结构有了差别,那这9个字节代表什么意思呢,这9个字节代表意思如下表:

状态位

文件号+页面ID

SLOT号

04

dd0000000100

02 00

    在上表中状态位04和以前介绍行的基本信息时的状态位A意义相同,bit1-31代表当前记录行是转移根,那这后面的文件+页面ID+slot,它最终指向数据搬到的地点,如何和实现关联起来呢?下面请看我的转换sql语句(我已经封装成存储过程sp_us_FPSinfo方便大家调用)

@H_102_404

@H_102_404@---------------------------------------------------------------------

@H_102_404@-- Author : HappyFlyStone

@H_102_404@-- Date   : 2009-11-30

@H_102_404@-- Version: Microsoft sql Server 2005 - 9.00.2047.00 (Intel X86)

@H_102_404@--      Apr 14 2006 01:12:25

@H_102_404@--          copyright (c) 1988-2005 Microsoft Corporation

@H_102_404@--          Enterprise Edition on Windows NT 5.2 (Build 3790: SP2)

@H_102_404@--      转载注明出处及相关信息@H_102_404@

@H_102_404@---------------------------------------------------------------------

CREATE PROC sp_us_FPSinfo

@FORWARDING_STUB BINARY(8)

AS

SELECT

    CAST(

        CONVERT(INT,SUBSTRING(@FORWARDING_STUB,6,1)) * POWER(2,8)

       + CONVERT(INT,5,1))

       AS VARCHAR)+'       : ' --File_num

   +CAST(

        (CONVERT(INT,4,24))

      + (CONVERT(INT,3,16))

      + (CONVERT(INT,2,8 ))

      + (CONVERT(INT,1))) AS VARCHAR)

      +'   : '  --Page_id

   +CAST(CAST(SUBSTRING(@FORWARDING_STUB,8,1) * POWER(2,8 ) +

           + SUBSTRING(@FORWARDING_STUB,7,1) AS INT ) AS VARCHAR)

    AS 'FILE_NUM:PAGE_ID:SLOT_ID'

GO

 

EXEC  sp_us_FPSinfo 0xDD00000001000200 --

@H_102_404

DROP PROC sp_us_FPSinfo

@H_102_404@/*

@H_102_404@FILE_NUM:PAGE_ID:SLOT

@H_102_404@--------------------------

@H_102_404@1       : 221    : 2

@H_102_404

@H_102_404@(1 行受影响@H_102_404@)

@H_102_404@*/

@H_102_404

    这个转换的结果表明本来在当前位置的记录已经转移到文件号为1页面221slot2的记录,在上面一开始我们查看221页面的信息,的确可以对应上,在这儿我们介绍一下上面的这一条记录转换的原因,因为我们一开始定义的表结构并插入记录在页面220的第一条记录是’aaaaa’,后因为修改列长,造成页面记录转换,根据上面的转换我们在页面221的第二条记录的确看到’aaaaa’的影子。

 

@H_502_1503@

状态位

Record Type

bit1-3

FORWARDING_STUB

0x04

2

 

3FORWARDED_RECORD 转移记录

       Record Type = FORWARDED_RECORD时表明当前是一条转换过来的记录。这正好和第二种类型相对,就是转移根指向的记录行,这个类型的记录行与基本行的结构还是有点差别的,前一部分差不多,只是最后有点不同。

@H_102_404

@H_102_404@

状态A

状态B

定长部分长度

定长数据

定长列数

NULL位图

不明待查

file_page_slot

(可用上面的过程转为转译,找到FORWARDING_STUB)

32@H_102_404@

00@H_102_404@

c907@H_102_404@

01000000 6161...... 20@H_102_404@

0300@H_102_404@

f8@H_102_404@

0100da87 0004

dc00 00000100 0000@H_102_404@

@H_102_404@3-有变长列及@H_102_404@NULL位图@H_102_404@

@H_102_404@2-1表示转移记录@H_102_404@

未用@H_102_404@

@H_102_404@1993

@H_102_404@ DATA

@H_102_404@3@H_102_404@

长度:@H_102_404@ceiling(3/8)

最后三位为@H_102_404@0

????

(暂时只发现只要转移过,都是这个值)

FILE_NUM:PAGE_ID:SLOT

--------------------

1       : 220    : 0@H_102_404@

    状态Abit1-3 1 表明当前记录行为转移根所指向的转移记录。它和主数据记录有差别就是在最后多了14个字节的数据,最后的8位通过我以上的过程(sp_us_FPSinfo),很明显的得到了转移根所有的文件页面slot号。

 

4INDEX_RECORD 索引记录

    我们先来生成一条索引记录,在对上表创建一个索引,脚本如下:

drop table testtb

go

create table testtb(id int ,pid int,col char(10))

go

insert into testtb select 9,'a'

insert into testtb select 3,'b'

insert into testtb select 4,'c'

insert into testtb select 5,'d'

insert into testtb select 6,'e'

go--clustered

create index idx_testtb_id on testtb(pid,id)

go

 

dbcc traceon(3604)

dbcc ind(testcsdn,-1)--> 230

dbcc page(testcsdn,232,1)

go

@H_102_404@/*

Slot 0,Length 20,DumpStyle BYTE

 

Record Type = INDEX_RECORD           Record Attributes =  NULL_BITMAP    

Memory Dump @0x446AC060

 

00000000:   16010000 00060000 00dc0000 00010004 †................        

00000010:   000300f8 ††††††††††††††††††††††††††††....                    

@H_102_404@*/

 

索引行有别于基本行的格式大体如下:

@H_102_404@

 

 

@H_102_404@

状态位

数据

文件+页面+槽号

列数(包括地址)

NULL位图

16

010000 00060000 00

dc0000 00010004 00

0300

f8

Bit41代表有NULL位图

Bit3-13代表是一条索引记录

Data

EXEC sp_us_FPSinfo 0xdc00000001000400

数据两列加上位置,共三列

 

FILE_NUM:PAGE_ID:SLOT_ID

--------------------------

1       : 220   : 0

 

 

 

 

@H_102_404

(未完,待续)

 

谢谢大家关注我的blog!!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐