2008存储结构之GAM,理解数据页结构4887王中王鉄算

来源:http://www.smjxgs.com 作者:王中王高手论坛 人气:89 发布时间:2019-08-10
摘要:数据页是包含已添加到数据库表中的用户数据的结构。 我们都很清楚SQL Server用8KB的页来存储数据,并且在SQL Server里磁盘 I/O 操作在页级执行。也就是说,SQLServer读取或写入所有数据页

数据页是包含已添加到数据库表中的用户数据的结构。

我们都很清楚SQL Server用8KB 的页来存储数据,并且在SQL Server里磁盘 I/O 操作在页级执行。也就是说,SQL Server 读取或写入所有数据页。页有不同的类型,像数据页,GAM,SGAM等。在这文章里,让我们一起来理解下数据页结构。

【IT168专稿】谈到GAM和SGAM,我们不得不从数据库的页和区说起。一个数据库由用户定义的空间构成,这些空间用来永久存储用户对象,例如数据库管理信息、表和索引。这些空间被分配在一个或多个操作系统文件中。

如前所述, 数据页有三种, 每个都以不同的格式存储数据。

SQL Server把数据记录存在数据页(Data Page)里。数据记录是堆表里、聚集索引里叶子节点的行。

  当我们创建一个数据库的时候,例如以缺省的方式CREATE DATABASE TESTDB,SQLServer自动帮我们创建好如下两个数据库文件。

SQL server 有行内数据页、行溢出数据页和 LOB 数据页。

数据页由3个部分组成。页头(标头),数据区(数据行和可用空间)及行偏移数组。

4887王中王鉄算盘奖结果 1  

与 SQL server 中的所有其他类型的页一样, 数据页的大小固定为 8 KB 或8192字节。

4887王中王鉄算盘奖结果 2

  这两个数据文件是实实在在的操作系统文件,其中一个是叫行数据文件,用来存储数据库的各种对象,另外一个是日志文件,从来记录数据变化的过程。

它们由三主要组件组成: 页面页眉、数据行和行偏移量数组, 如图6-4 所示。

在我们讨论在SQL Server里,数据页内部结构具体是什么样之前,我们来创建一个表并插入一些记录。

  从逻辑角度而言,数据库的最小存储单位为页即8kb。

4887王中王鉄算盘奖结果 3

 

  数据库被分成若干逻辑页面(每个页面8KB),并且在每个文件中,所有页面都被连续地从0到x编号,其中x是由文件的大小决定的。我们可以通过指定一个数据库ID、一个文件ID、一个页码来引用任何一个数据页。每个数据页则用来存储表和索引,以及相关的数据库管理信息。

 

 1 USE [InternalStorageFormat]
 2 GO
 3 
 4 IF EXISTS ( SELECT  *
 5             FROM    sysobjects
 6             WHERE   id = OBJECT_ID(N'[dbo].[Customers]')
 7                     AND OBJECTPROPERTY(id, N'IsUserTable') = 1 )
 8     DROP TABLE dbo.Customers
 9 
10 CREATE TABLE Customers
11 (
12    FirstName CHAR(50) NOT NULL,
13    LastName CHAR(50) NOT NULL,
14    Address CHAR(100) NOT NULL,
15    ZipCode CHAR(5) NOT NULL,
16    Rating INT NOT NULL,
17    ModifiedDate DATETIME NOT NULL,
18 )
19 GO
20 
21 
22 INSERT INTO dbo.Customers
23         ( FirstName ,
24           LastName ,
25           Address ,
26           ZipCode ,
27           Rating ,
28           ModifiedDate
29         )

30 VALUES  ( 'Woody' , -- FirstName - char(50)
31           'Tu' , -- LastName - char(50)
32           'ZUOQIAO YOUXI TOWN LINHAI CITY' , -- Address - char(50)
33           '0000' , -- ZipCode - char(5)
34           1 , -- Rating - int
35           '2015-05-07 10:09:51'  -- ModifiedDate - datetime
36         )
37         go 2

  我们顺着上面数据文件的路径可以找到该文件,观察一下新建的数据文件的大小为:

页标题

现在我们要找出SQL Server给这个表分配的页有哪些,这个就要用到非文档的命令DBCC IND。
它的语法如下:

  2.18 MB (2,293,760 字节)=2,293,760b/8kb=280个页面=35个区

 

DBCC IND 命令用于查询一个存储对象的内部存储结构信息,该命令有4个参数, 前3个参数必须指定。语法如下:
DBCC IND ( { 'dbname' | dbid }, { 'objname' | objid },{ nonclustered indid | 1 | 0 | -1 | -2 } [, partition_number] )
第一个参数是数据库名或数据库ID。
第二个参数是数据库中的对象名或对象ID,对象可以是表或者索引视图。
第三个参数是一个非聚集索引ID或者 1, 0, 1, or 2. 值的含义:
 0: 只显示对象的in-row data页和 in-row IAM 页。
 1: 显示对象的全部页, 包含IAM 页, in-row数据页, LOB 数据页row-overflow 数据页 . 如果请求的对象含有聚集所以则索引页也包括。
 -1: 显示全部IAM页,数据页, 索引页 也包括 LOB 和row-overflow 数据页。
 -2: 显示全部IAM页。
 Nonclustered index ID:显示索引的全部 IAM页, data页和索引页,包含LOB和 row-overflow数据页。
为了兼容sql server 2000,第四个参数是可选的,该参数用于指定一个分区号.如果不给定值或者给定0, 则显示全部分区数据。
和DBCC PAGE不同的是, SQL Server运行DBCC IND不需要开启3604跟踪标志.

  数据库进行空间管理的最小单位为区(extents)。

如图6-4 所示, 页标题占据每个数据页的前96个字节 (为数据、行开销和行偏移保留8096个字节)。表6-5 列出了检查页标题时显示的一些信息。

我们来执行下列的命令:

  一个区由8个逻辑上连续的页面组成(64KB的空间)。为了能够更有效地分配空间,SQL Server 2008不会为少量的数据向数据表分配整区的空间。SQL Server 2008有两种类型的区。

4887王中王鉄算盘奖结果 4

1 DBCC IND('InternalStorageFormat','Customers',-1)

  统一类型的区 这些区为单个对象所有,区中所有的8个数据页只能被所属对象使用。

 

SQL Server会给我们如下的输出结果:
4887王中王鉄算盘奖结果 5

  混合类型的区 这些区能为最多8个对象共享。

行内数据的数据行

可以看到有2条记录,一条记录为页面类型(PageType)为10的页和一条记录为页面类型(PageType)为1的页。页面类型(PageType)10是IAM页,页面类型(PageType)1是数据页,它的页ID是79.

  SQL Server为新的表或索引从混合类型的区中分配页面。当该表或索引增长到8个页面时,以后所有的分配都使用统一类型的区。

 

关于数据库页类型如下所示:

  当一张表或一个索引需要更多的空间时,SQL Server需要找到能够用来分配的空间。如果该表或索引整体仍然少于8个页面,SQL Server必须找到能够用来分配的混合类型区构成的空间。如果表或索引有8个页面或更大,SQL Server必须找到一个自由的统一类型的区。

页标题后面是存储表实际数据行的区域。单个数据行的最大大小为8060字节的行内数据。

  • 1 Data page 堆表和聚集索引的叶子节点数据
  • 2 Index page 聚集索引的非叶子节点和非聚集索引的所有索引记录

  • 3 Text mixed page A text page that holds small chunks of LOB values plus internal parts of text tree. These can be shared between LOB values in the same partition of an index or heap.

  • 4 Text tree page A text page that holds large chunks of LOB values from a single column value.

  • 7 Sort page 排序时所用到的临时页,排序中间操作存储数据用的。

  • 8 GAM page 全局分配映射(Global Allocation Map,GAM)页面 这些页面记录了哪些区已经被分配并用作何种用途。

  • 9 SGAM page 共享全局分配映射(Shared Global Allocation Map,GAM)页面 这些页面记录了哪些区当前被用作混合类型的区,并且这些区需含有至少一个未使用的页面。

  • 10 IAM page  有关每个分配单元中表或索引所使用的区的信息

  • 11 PFS page  有关页分配和页的可用空间的信息

  • 13 boot page 记录了关于数据库的信息,仅存于每个数据库的第9页

  • 15 file header page 记录了关于数据库文件的信息,存于每个数据库文件的第0页

  • 16 DCM page 记录自从上次全备以来的数据改变的页面,以备差异备份

  • 17 BCM page 有关每个分配单元中自最后一条 BACKUP LOG 语句之后的大容量操作所修改的区的信息

  SQL Server使用两种特殊类型的页面来记录哪些区已经被分配出去了,哪些类型(混合类型或统一类型)的区可供使用:

行还可以在单独的页上存储行溢出和 LOB 数据。

现在我们来看看79号类型为1的数据页里存放的数据,这个就要用到DBCC PAGE命令,它的语法如下:

  全局分配映射(Global Allocation Map,GAM)页面 这些页面记录了哪些区已经被分配并用作何种用途。一个GAM页面在它所覆盖空间里针对每一个区都有一个数据位。如果数据位为0,那么对应的区正在使用;如果该数据位为1,那么该区为自由区。一个GAM页面除了页面头部和其他一些需要记入的开销大概有8 000字节或者说64 000位空间可用,所以每个GAM页面可以覆盖64 000个区,也就是大约4GB的数据。这意味着一个文件的每4GB空间对应一个GAM页面。

在给定页上存储的行数根据表结构和存储的数据而变化。

dbcc page 命令读取数据页结构的命令DBCC Page。
该命令为非文档化的命令,具体如下:
  DBCC Page ({dbid|dbname},filenum,pagenum[,printopt])
  具体参数描述如下:
  dbid 包含页面的数据库ID
  dbname 包含页面的数据库的名称
  filenum 包含页面的文件编号
  pagenum 文件内的页面
  printopt 可选的输出选项;选用其中一个值:
  0:默认值,输出缓冲区的标题和页面标题
  1:输出缓冲区的标题、页面标题(分别输出每一行),以及行偏移量表
  2:输出缓冲区的标题、页面标题(整体输出页面),以及行偏移量表
  3:输出缓冲区的标题、页面标题(分别输出每一行),以及行偏移量表;每一行
  后跟分别列出的它的列值
  要想看到这些输出的结果,还需要设置DBCC TRACEON(3604)。

  共享全局分配映射(Shared Global Allocation Map,SGAM)页面 这些页面记录了哪些区当前被用作混合类型的区,并且这些区需含有至少一个未使用的页面。就像一个GAM页面,每一个SGAM页面覆盖了大约64 000个区,也就是大约4GB的数据。一个SGAM页面在它所覆盖空间里针对每一个区都有一个数据位。如果数据位为1,那么对应的被使用的区为混合类型,并且该区有一些自由页面;如果数据位为0,那么对应的区不是一个混合类型的区,或者虽然是一个混合类型的区,但是所有的页面都已被使用了。

具有所有固定长度列的表始终可以存储每页相同的行数;

我们来执行下列的命令:

  表4-2显示了基于每一个区当前的使用情况,在GAM和SGAM中该区所对应的比特位模式。

可变长度行可以根据输入数据的实际长度, 存储尽可能多的行。

1 DBCC TRACEON(3604)
2 DBCC PAGE(InternalStorageFormat,1,79,3)
3 GO    
区的当前使用情况 GAM比特位设置 SGAM比特位设置
自由,未使用 1 0
统一类型或已全部使用的混合区 0 0
含有自由页面的混合区 0 1

保持行长更短可以使更多行适合页面, 从而减少 i/o 并增加所需数据在缓存中的可能。

SQL Server会给我们包含4个部分的输出。第1部分是BUFFER,里面是一些内存分配信息,对此我们没多少兴趣。下一部分是固定96 bytes大小的页头(page header),页头(page header)会类似如下显示:

  如果SQL Server需要找到一个新的完全没有使用的区,那么它可以使用任何一个在GAM页面中对应的比特位值为1的区。如果SQL Server需要找到一个有着可用空间(有一个或多个自由页面)的混合类型的区,那么它可以寻找一个对应的GAM中的值为0、SGAM中的值为1的区。如果不存在有可用空间的混合类型的区,SQL Server会使用GAM页面来寻找一个全新的区并将其分配为混合类型的区,然后使用该区中的一页。如果根本没有自由区,那么这个文件已经满了。

 

4887王中王鉄算盘奖结果 6

第0页 第1页 第2页 第3页 第4页 第5页 第6页 第7页
m_type=15 m_type=11 m_type=8 m_type=9 m_type=0 m_type=0 m_type=16 m_type=17
头文件页 PFS页 GAM页 SGAM页 保留页 保留页 DCM页 BCM页

行偏移量数组

页头相关字段的含义:

  SQL Server能够迅速地锁定一个文件中的GAM页面,因为它总是位于任何数据库文件的第三页上(页码为2)。SGAM页面是在第四页上(页码为3)。下一个GAM页面出现在第一个GAM页面(页码为2)以后的每511 230个页面中,并且下一个SGAM页面出现在第一个SGAM页面(页码为3)以后的每511 230个页面中。每一个数据库文件的页码为0的页面是文件头页面,并且每个文件仅有一页。页码0是头文件页,页码1是页面自由空间页(Page Free Space,PFS)。

 

  • Page @0x08F84000            同BUFFER中的bpage地址
  • m_pageId = (1:79)              数据页号     
  • m_headerVersion = 1         头文件版本号,一直为1          
  • m_type = 1                          页面类型,1为数据页面
  • m_typeFlagBits = 0x4         数据页和索引页为4,其他页为0        
  • m_level = 0                         该页在索引页(B树)中的级数
  • m_flagBits = 0x8000          页面标志
  • m_objId (AllocUnitId.idObj) = 46                       同Metadata: ObjectId             
  • m_indexId (AllocUnitId.idInd) = 256                  同Metadata: IndexId
  • Metadata: AllocUnitId = 72057594040942592  存储单元的ID,sys.allocation_units.allocation_unit_id                              

  • Metadata: PartitionId = 72057594039304192   数据页所在的分区号,sys.partitions.partition_id                             

  • Metadata: IndexId = 0                                        页面的索引号,sys.objects.object_id&sys.indexes.index_id

  • Metadata: ObjectId = 277576027                      该页面所属的对象的id,sys.objects.object_id
  • m_prevPage

    (0:0)                  该数据页的前一页面;主要用在数据页、索引页和IAM页

  • m_nextPage = (0:0)                  该数据页的后一页面;主要用在数据页、索引页和IAM页

  • pminlen = 221                          定长数据所占的字节数
  • m_slotCnt = 2                           页面中的数据的行数
  • m_freeCnt = 7644                    页面中剩余的空间
  • m_freeData = 544                    从第一个字节到最后一个字节的空间字节数

  • m_reservedCnt = 0                   活动事务释放的字节数

  • m_lsn = (255:8406:2)                日志记录号
  • m_xactReserved = 0                 最新加入到m_reservedCnt领域的字节数
  • m_xdesId = (0:0)                       添加到m_reservedCnt的最近的事务id
  • m_ghostRecCnt = 0                 幻影数据的行数
  • m_tornBits = 0                         页的校验位或者被由数据库页面保护形式决定分页保护位取代

  在SQLServer2008的每一个数据库中的前八页顺序都是固定的。

行偏移量数组是2字节项的块, 每个条目表示相应数据行开始的页面上的偏移量。

再来看下页面相关分配情况:

  除了第9页为数据库的BOOT页以外,从第8页到第173页为SQLServer2008内部系统表的相关存储信息,然后从第174页到第279页为未分配页面。因为第一页从0开始,所以刚好280页,即和我们看到的数据库数据文件的大小完全相等。

每行在这个数组中都有一个2字节的条目 (正如前面所讨论的, 当您阅读每行所需的10个开销字节时)。

 4887王中王鉄算盘奖结果 7

第8页

虽然这些字节没有存储在数据行中, 但它们确实会影响适合页面的行数。

  • GAM (1:2) = ALLOCATED                                                   在GAM页上的分配情况
  • SGAM (1:3) = ALLOCATED                                                 在SGAM页上的分配情况
  • PFS (1:1) = 0x61 MIXED_EXT ALLOCATED  50_PCT_FULL 在PFS页上的分配情况,该页为50%满,                       

  • DIFF (1:6) = CHANGED

  • ML (1:7) = NOT MIN_LOGGED   

第8页

 

接下来就是用于存放实际数据的槽(slot),每条记录存放一个槽(slot)里。0号槽在页里拥有第1条数据,1号槽拥有第2条数据,以此类推。通过下面的图片,你可以看到我们记录大小是224 bytes,217 bytes(50 50 100 5 4 8) 的定长和7 bytes 的系统行开销。

第8页

行偏移量数组指示页上行的逻辑顺序。

4887王中王鉄算盘奖结果 8

第N页

例如, 如果表具有聚集索引, SQL server 将按聚集索引键的顺序存储这些行。

页的最后一部分是行偏移数组表,我们可以用参数为1的DBCC PAGE命令来,在输出信息的底部获得。

第173页

这并不意味着行按聚集索引键的顺序物理地存储在页面上。

执行如下的命令:

第279页

相反, 偏移量数组中的插槽0引用聚集索引键顺序中的第一行, 插槽1引用第二行, 依此类推。

1 DBCC TRACEON(3604)
2 DBCC PAGE(InternalStorageFormat,1,79,3)
3 GO    

m_type=1

正如您在检查实际页面时所看到的那样, 这些行的物理位置可以位于页面的任何位置。

SQL Server在输出信息的底部,给我们如下的信息:

m_type=13

 

4887王中王鉄算盘奖结果 9

m_type in (1,2,10)

查看数据页

这个行偏移表,应该从下往上读。每条槽条目是一个2 bytes长的指针指向页里槽偏移量。这里我们插入了2条记录,所以表里有2个槽条目。第1条记录指向第96 bytes,刚好在页头后。这个行偏移表可以帮助我们管理页面的记录。在页里的行偏移表里,每条记录需要2 bytes的大小来存储。于此类似,在堆表上建立的非聚集索引,每个非聚集索引行里都包含一个物理指针映射回堆表里的行记录。这个物理指针是[文件号:页号:槽号](file:page:solt)的结构,因此在读取页的时候,可以找到堆表里的对应行,再通过行偏移表里槽号里的偏移量,就可以在页里读取到对应的行记录。如果我们要修改页中间的记录,我们并不一定需要重组整个页,我们只要修改偏移表里偏移量即可。

N/A

 

4887王中王鉄算盘奖结果 10

Data页

可以使用 DBCC 页命令查看数据页的内容, 

在页头我们看到当前页面还有7644 bytes可以用,我们一起来验证下。

Boot页

这允许您查看数据库中任何给定页的页标题、数据行和行偏移表。

(8 * 1024) - 96 - (217 * 2)-(7 * 2)-(2 * 2)=7644 bytes

主要为内部系统表相关信息

只有系统管理员才能使用 DBCC 页。

8 * 1024 = 页的总大小,8K

未分配

但是, 由于通常不需要查看数据页的内容, 因此在 SQL server 文档中找不到有关 DBCC 页的信息。

         96 = 页头大小 96 bytes       

  以下截图是通过SQLServer2008的Internals Viewer插件看到的整体页面结构,该插件是从

不过, 如果您想使用它, 下面是语法:

 217 * 2 = 每条记录的总长 * 记录数

  备注:TESTDB为新创建的空数据库,没有任何用户自定义对象,直到有建表脚本为止;

DBCC PAGE ({dbid | dbname}, filenum, pagenum[, printopt])

     7 * 2 = 每条记录的系统行开销 * 记录数

4887王中王鉄算盘奖结果 11  

 

     2 * 2 = 行偏移表里每槽占用字节数 * 记录数

 

DBCC 页面命令包含表6-6 中显示的参数。

现在我们已经知道了页的结构,我们一起来小结下。

       关于数据库页类型如下所示:

清单6-4 中的代码和结果显示来自 DBCC 页的示例输出, 其 printopt 值为1。

页是 8KB 的大小,即 8192 bytes,固定 96 bytes的大小给页头使用,接下来是具体的数据以槽的方式存储。数据记录的最大长度是 8060 bytes(包括 7 bytes的系统行开销),因此一条记录中你拥有的最大字节数是 8053 bytes。下列的表创建语句会失败。

类型 页面类型名称 页面类型描述
1 Data page 堆表和聚集索引的叶子节点数据
2 Index page 聚集索引的非叶子节点和非聚集索引的所有索引记录
3 Text mixed page A text page that holds small chunks of LOB values plus internal parts of text tree. These can be shared between LOB values in the same partition of an index or heap.
4 Text tree page A text page that holds large chunks of LOB values from a single column value.
7 Sort page 排序时所用到的临时页,排序中间操作存储数据用的。
8 GAM page 全局分配映射(Global Allocation Map,GAM)页面 这些页面记录了哪些区已经被分配并用作何种用途。
9 SGAM page 共享全局分配映射(Shared Global Allocation Map,GAM)页面 这些页面记录了哪些区当前被用作混合类型的区,并且这些区需含有至少一个未使用的页面。
10 IAM page. 有关每个分配单元中表或索引所使用的区的信息
11 PFS page. 有关页分配和页的可用空间的信息
13 boot page. 记录了关于数据库的信息,仅存于每个数据库的第9页
15 file header page 记录了关于数据库文件的信息,存于每个数据库文件的第0页
16 DCM page 记录自从上次全备以来的数据改变的页面,以备差异备份
17 BCM page 有关每个分配单元中自最后一条 BACKUP LOG 语句之后的大容量操作所修改的区的信息

请注意, DBCC TRACEON (3604) 指示 SQL server 将结果返回给客户端。

1 CREATE TABLE Maxsize(
2 id         CHAR(8000) NOT NULL,
3 id1        CHAR(54) NOT NULL
4 )

  实际上SQLServer还包括一些未公开的页面类型,例如type 19,type 14等等。

如果没有此 traceflag, 则不会为 DBCC 页命令返回输出。

4887王中王鉄算盘奖结果 12

  本章我们主要介绍GAM页和SGAM页,其他页面类型会稍后介绍。

 

剩下的 36 bytes (8192-96-8060)保留给槽数组(Slot array)或者任何转发行返回指针(forwarding row back pointer)(每条10 bytes)。这就意味一个页不一定就能保存18(36/2)条记录。槽数组(Slot array)根据你的记录数从下往上增长。如果记录长度小,页里就可以存储更多的记录,偏移表也会自下而上占用更多的空间。 

  那么如何查看页面信息呢,从SQLServer2000起便开始提供了一个读取数据页结构的命令DBCC Page。该命令为非文档化的命令,具体如下:

4887王中王鉄算盘奖结果 13

参考文章:

http://www.sqlservercentral.com/blogs/practicalsqldba/2012/08/12/sql-server-understanding-the-data-page-structure/

  DBCC Page ({dbid|dbname},filenum,pagenum[,printopt])

正如所看到的, DBCC 页的输出分为四主要部分: 缓冲区、页标题、数据和偏移量表 (实际上是偏移量数组)。

  具体参数描述如下:

缓冲区部分显示有关给定页的缓冲区的信息。

  dbid               包含页面的数据库ID

此上下文中的缓冲区是管理页的内存结构, 本节中的信息仅在页处于内存中时才相关。

  dbname       包含页面的数据库的名称

 

  filenum         包含页面的文件编号

从 DBCC 页的 "输出" 页标题部分显示页上所有标题字段的数据。

  pagenum      文件内的页面

(表6-5 显示了大多数这些字段的含义。数据节包含每行的信息。

  printopt            可选的输出选项;选用其中一个值:

使用具有 printopt 值1或3的 DBCC 页表示插槽位置, 即每行的6个表存储257、页上的行的偏移量和行的长度。

                            0:默认值,输出缓冲区的标题和页面标题

行数据分为三部分。

                           1:输出缓冲区的标题、页面标题(分别输出每一行),以及行偏移量表

 

                           2:输出缓冲区的标题、页面标题(整体输出页面),以及行偏移量表

左列指示显示数据所在行内的字节位置。

                           3:输出缓冲区的标题、页面标题(分别输出每一行),以及行偏移量表;每一行后跟分别列出的它的列值

中间部分包含存储在页面上的实际数据, 每五列显示八个十六进制数字。

  如果要想看到这些输出的结果,还需要设置DBCC TRACEON(3604)。

最右边的列包含数据的 ASCII 字符表示形式。 此列中只有字符数据是可读的, 尽管可能会显示某些其他数据。

  如前文所述,GAM页一定存在于该数据库的第二个页面,SGAM页则一定存在于该数据库的第三个页面;而每一个数据库都会存在文件编号为1的数据库文件,所以我们执行以下命令即可。

"偏移量表" 部分显示页面末尾的行偏移量数组的内容。

[html] view plaincopy

 

  1. DBCC TRACEON(3604)  
  2. DBCC PAGE(TESTDB,1,2,1)  —查看GAM页信息  
  3. DBCC PAGE(TESTDB,1,3,1)  —查看SGAM页信息  
  4. DBCC PAGE(TESTDB,1,2,2)  —查看GAM页信息和整体输出页面  
  5. DBCC PAGE(TESTDB,1,3,2)  —查看SGAM页信息和整体输出页面  
  6. DBCC PAGE(TESTDB,1,2,3)  —查看GAM页信息及相应列值  
  7. DBCC PAGE(TESTDB,1,3,3)  —查看SGAM页信息及相应列值  
  8. DBCC PAGE(TESTDB,1,2,1) WITH TABLERESULTS  —以表格形式查看SGAM页信息及相应列值  
  9. DBCC PAGE(TESTDB,1,3,1) WITH TABLERESULTS  —以表格形式查看SGAM页信息及相应列值  

在 DBCC 页的输出中, 您可以看到此页包含23行, 第一行 (由插槽0指示) 从偏移量 1585 (0x631) 开始。

 

实际存储在页面上的第一行实际上是6行, 而行偏移量数组中的偏移量为96。printopt 值为1的 DBCC 页显示 "插槽编号" 顺序中的行, 即使可以从每个插槽的偏移量中看到, 也不是在页面上实际存在行的顺序。

  我们可以看到一个完整的页面分为四个部分;BUFFER、PAGE HEADER、DATA和OFFSET TABLE。

如果使用 printopt 值为2的 DBCC 页, 则会看到页的所有8096个字节 (标头之后) 的转储, 按它们存储在页面上的顺序排列。

  让我们首先从GAM页开始看起:

 

  BUFFER部分:

  显示给定页面的缓冲信息,是内存中的结构,用于管理页面,该信息仅当该页面处于内存时才有意义。关于这个部分我们知之甚少,基本上无法找到相关材料。

BUF @0x03585CD8 每一次清空缓存再次查询,地址都会改变
bpage = 0x060B4000 每一次清空缓存再次查询,地址都会改变
bhash = 0x00000000 相对不变
bpageno = (1:2) 当前页面地址
bdbid = 8 sys.databases.database_id
breferences = 1 每一次清空缓存再次查询,地址都会改变
bUse1 = 41490 每一次清空缓存再次查询,地址都会改变
bstat = 0xc00009 相对不变
blog = 0x59ca2159 相对不变
bnext = 0x00000000 相对不变

  

  PAGE HEADER部分:

  PAGE HEADER部分显示的是该页面上的所有报头字段的数据

4887王中王鉄算盘奖结果 14  

  PAGE HEADER这部分内容只有通过DBCC PAGE(TESTDB,1,2,2)即整体输出页面才能够展现;通过与上面表格的对照,我们勉强能识别一些相关存储信息;当这部分缺乏官方文档的支持,为了避免无谓的猜测,所以暂时就不做深入探讨了。

4887王中王鉄算盘奖结果 15  

  DATA 部分

4887王中王鉄算盘奖结果 16  

   DATA部分一般分为若干插槽号(Slot),如果是数据页或索引页的话,可以理解为一行记录,SQLServer通过文件号 页面号 插槽号用来唯一标识表中的每一条记录。但在GAM页中我们可以把Slot 0理解为GAM页的保留页,共计94个字节。

  从第194个字节开始(页面总是从第0个字节开始的),到第196个字节,这三个字节代表已分配的分区的情况。即0000C0。

  我们再来看一下DBCC PAGE(TESTDB,1,2,3)的执行结果。

4887王中王鉄算盘奖结果 17  

  上面显示从第1页到第168页已分配,而第176页到272页未分配,和DBCC PAGE(TESTDB,1,2,2)显示的194个页面似乎有些矛盾,实际上是不矛盾的。如前文所述,GAM对未使用的分区标识为0,而对已分配的分区标识为1

  1个分区=64页,因为前128个页面均已分配,所以前两个字节为00 00

  从第128个页面起到第175个页面也均已分配,实际上为6个区为0也就是说连续6个bit为0,一个字节为8个bit,最后两个bit为11,所以该字节为0000 0011,在此需要反转一下相关二进制位;反转之后为1100 0000即为C0。

  最后让我们用Internals Viewer插件看一下GAM页的全貌吧。

4887王中王鉄算盘奖结果 18  

 

  SGAM页面

PAGE: (1:3)

BUFFER:
BUF @0x0358A7F4
bpage = 0x062AE000             bhash = 0x00000000              bpageno = (1:3)
bdbid = 8                      breferences = 3                       bUse1 = 14428
bstat = 0xc00009               blog = 0x21212159              bnext = 0x00000000
                                                               
PAGE HEADER:                                                   
Page @0x062AE000                                               
m_pageId = (1:3)               m_headerVersion = 1             m_type = 9
m_typeFlagBits = 0x0           m_level = 0                     m_flagBits = 0x200
m_objId (AllocUnitId.idObj)=99 m_indexId (AllocUnitId.idInd)=0 Metadata: AllocUnitId=6488064
Metadata: PartitionId = 0      Metadata: IndexId = 0           Metadata: ObjectId = 99
m_prevPage = (0:0)             m_nextPage = (0:0)              pminlen = 90
m_slotCnt = 2                  m_freeCnt = 6                   m_freeData = 8182
m_reservedCnt = 0              m_lsn = (18:435:5)              m_xactReserved = 0
m_xdesId = (0:0)               m_ghostRecCnt = 0               m_tornBits = 177043542
Allocation Status              
GAM (1:2)=ALLOCATED            SGAM (1:3)=NOT ALLOCATED          PFS(1:1)=0x44 ALLOCATED 100_PCT_FULL
DIFF (1:6) = CHANGED           ML (1:7) = NOT MIN_LOGGED         

DATA:
Slot 0, Offset 0x60, Length 94, DumpStyle BYTE
Record Type = PRIMARY_RECORD         Record Attributes =                  
Memory Dump @0x4F32C060
00000000:   00005e00 00000000 00000000 00000000 ?..^.............         
00000010:   00000000 00000000 00000000 00000000 ?................         
00000020:   00000000 00000000 00000000 00000000 ?................         
00000030:   00000000 00000000 00000000 00000000 ?................         
00000040:   00000000 00000000 00000000 00000000 ?................         
00000050:   00000000 00000000 00000000 0000??????..............           

Slot 1, Offset 0xbe, Length 7992, DumpStyle BYTE
Record Type = PRIMARY_RECORD         Record Attributes =                  
Memory Dump @0x4F32C0BE
00000000:   0000381f 20ee2000 00000000 00000000 ?..8. . .........         
00000010:   00000000 00000000 00000000 00000000 ?................ 
00001F30:   00000000 00000000 ???????????????????........           

 
   以下为DBCC PAGE(TESTDB,1,3,3)得到的相关信息,有兴趣的可以和20ee20做一下对比。

(1:0)        - (1:32)       = NOT ALLOCATED                               
(1:40)       -              =     ALLOCATED                               
(1:48)       - (1:64)       = NOT ALLOCATED                               
(1:72)       - (1:88)       =     ALLOCATED                               
(1:96)       -              = NOT ALLOCATED                               
(1:104)      - (1:120)      =     ALLOCATED                               
(1:128)      - (1:160)      = NOT ALLOCATED                               
(1:168)      -              =     ALLOCATED                               
(1:176)      - (1:272)      = NOT ALLOCATED

 
  最后让我们用Internals Viewer插件看一下SGAM页的全貌吧。

4887王中王鉄算盘奖结果 19  

  总结一下,关于GAM和SGAM页比较困难的地方:

  1、 关于GAM和SGAM页中的BUFFER信息基本无法理解,也找不到相关材料。

  2、 PAGE HEADER的部分信息和Slot 0中的一部分信息,也无法找到相关材料。

  3、 SGAM页中的NOT ALLOCATED实际上是统一类型区或者已使用完的混合类型的区,而ALLOCATED实际上为含有自由页面的混合区。

  4、 GAM页中0代表已分配,1代表自由区;和一般的标志位的含义刚好相反。

  5、 GAM和SGAM实际上只分配了280个页面,即35个区;显示出来的数据内容虽然很多,但后面的分区信息实际上是不存在的。

  6、 GAM和SGAM通过DBCC的printopt为3的属性显示出来的页面分配信息看似是断号的。

  7、 GAM和SGAM的区信息的字节是通过二级制反转得到的。

  GAM和SGAM页的总的大小为8192个字节;文件头为96个字节,slot 0为94个字节,slot 1的头部的系统信息为4个字节,尾部的系统信息为10个字节,所以有效存储应为7988个字节,63904个区,511230个页;事实上当数据文件超过约4G的时候,我们将能在第511232页、 第511233页分别找到其对应的GAM、SGAM页面。

本文由4887王中王鉄算盘奖结果发布于王中王高手论坛,转载请注明出处:2008存储结构之GAM,理解数据页结构4887王中王鉄算

关键词:

最火资讯