SQL SERVER大话存储结构(1)_数据页类型及页面指令分析
1 数据页的类型
Id | User_for | Page Type | Page Name | Description |
1 | 实际数据 | 数据页 | Data Page | 堆表或者聚集索引的叶子节点 |
2 | 索引页 | Index Page | 聚集索引的分支节点或者非聚集索引 | |
3 | LOB | LOB | 用来存放大型对象数据类型:text , image ,varchar(max) , varbinary(max)等 | |
4 | 行溢出页 | Row Overflow Page | 只能存储单一text或者image列数据块 | |
5 | 管理数据页数据 | GAM页 | Global Allocation Map | 管理统一区的位图 |
6 | SGAM页 | Shared Global Allocation Map | 管理混合区的位图 | |
7 | IAM页 | Index Allocation Map | 分配单元分配到的区 | |
8 | PFS页 | Page Free Space | 可用空间 | |
9 | 备份相关的数据 | DCM页 | Differential Changed Map | 自最后一条backup database 语句之后更改的区的信息 |
10 | BCM页 | Bulk Changed Map | 自最后一条backup log语句之后的大容量操作所修改的区的信息 |

1.1 PFS
- bit 0-2位,描述该页还有多少空闲空间
- 0x00 is empty
- 0x01 is 1 to 50% full
- 0x02 is 51 to 80% full
- 0x03 is 81 to 95% full
- 0x04 is 96 to 100% full
- bit 3 (0x08): 该数据页是否存在鬼影记录(ghost records:http://www.cnblogs.com/lyhabc/archive/2013/06/16/3138214.html)?
- bit 4 (0x10): 是否是IAM页?
- bit 5 (0x20): 是否是混合页?
- bit 6 (0x40): 是否已分配使用?
- Bit 7 保留,未使用,无实际含义
1.2 GAM & SGAM
1.2.1 GAM
- Bit=1,标识当前的区是空闲的,可以用来分配;
- Bit=0,标识当前的区以及被数据使用。
1.2.2 SGAM
- 当存储引擎分配一个统一区时,在GAM页中寻找标记为1的页面,把标记修改为0,SGAM页中的标记位不做变动,保持为0;
- 当存储引擎分配一个混合区时,在GAM页中寻找标记为1的页面,把标记修改为0,SGAM页中的标记位从0修改1;
- 当存储引擎寻找一个有空闲页的混合区是,直接在SGAM页中查找标记位1对应的数据页;如果没有找到,则会重新分配一个混合区。
1.2.3 IAM
- IN_ROW_DATA
- 存储堆或索引分区,即heap和B-tree。
- LOB_DATA
- 存储大型对象 (LOB) 数据类型,例如 xml、varbinary(max) 和 varchar(max)。
- ROW_OVERFLOW_DATA
- 存储超过 8,060 字节行大小限制的 varchar、nvarchar、varbinary 或 sql_variant 列中存储的可变长度数据。
每个有数据的表格,至少有一个 IAM页来管理 IN_ROW_DATA的存储情况,如果表格里有LOB_DATA,则会多一个IAM页来管理LOB_DATA,ROW_OVERFLOW_DATA也是一样。
2 数据页结构

2.1 页头
占用96字节,存储跟该页面相关的系统数据。
页头的内容如下:
2.2 行记录
- 存储数据行记录以及索引数据
- 行记录也可以在独立页面上存储,比如行溢出数据即LOB数据
2.3 空闲空间
- 除去页头,行记录,以及偏移量剩下的空间,提供给行记录及行偏移量使用
2.4 行偏移量
- 行偏移是一个个小块组成的,每个小块2个字节,表示数据行从第几个字节后开始记录,也就是距离页头多少偏移量开始记录
- 存储方式是从有往左存储,用槽位来描述,slot 0 ,slot 1 ....
- 行偏移量记录的内容是什么呢?该行记录从哪个字节开始,一般情况下,slot 1 从第96个字节后开始
- 常说的聚集索引存储顺序是物理排序,指的不是行记录物理排序,而是行偏移量物理排序,数据页中,行记录都是顺序往后添加的,通过修改行偏移量来达到聚集索引的顺序查找
3 查询数据页存储格式的途径
3.1 dbcc ind
3.1.1 语法说明
- DBCC TRACEON(2588)
- DBCC HELP('ind')
- DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
- dbcc IND ( { 'dbname' | dbid }, { 'objname' | objid }, { nonclustered indid | 1 | 0 | -1 | -2 } [, partition_number] )
- DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
- -2:返回所有IAM页,基于管理行内数据页,行溢出数据页及大对象数据页的IAM页
- -1:返回所有IAM页及数据页。
- 0:返回管理行内数据页的IAM页,行内数据页
- 1:返回聚集索引的数据页信息及IAM页信息(同-1)
- 2:返回第1个非聚集索引的数据页信息及IAM页信息
- 3:返回第2个非聚集索引的数据页信息及IAM页信息
- ...
- n:返回第(n-1)个非聚集索引的数据页信息及IAM页信息(n>1)
3.1.2 测试案例
- create table tbpage_c(id int identity(1,1) not null primary key ,namea varchar(6000),nameb varchar(3000),descriptions text)
- #name_a INSERT 6000个字符,name_b INSERT 3000个字符,descriptions INSERT 100个字符
- INSERT INTO tbpage_c(NAMEA,nameb,descriptions)
- select
- substring(stuff((select name+',' from master.dbo.spt_values for xml path('')),1,1,''),1,6000) ,
- substring(stuff((select name+',' from master.dbo.spt_values for xml path('')),1,1,''),1,3000) ,
- substring(stuff((select name+',' from master.dbo.spt_values for xml path('')),1,1,''),1,100)
dbcc ind('dbpage','tbpage_c',-2)
选项为-2,显示表格的所有IAM页面。由于表格存在行溢出及大对象列,所以会有其相对应的IAM页面,故可以看到有3个IAM,分别为 In-row data ,Row-overflow data ,LOB data。

- 数据页号310,309属于 In-row data 类型。309记录实际数据,310记录In-row data实际数据页的分布情况。
- 数据页号307,308属于 Row-overflow data 类型。307记录实际数据,308记录 Row-overflow data 实际数据页的分布情况。
- 数据页号305,306属于 LOB data 类型。305记录实际数据,306记录 LOB data 实际数据页的分布情况。



3.2 dbcc page
3.2.1 语法说明
- DBCC TRACEON(2588)
- DBCC HELP('PAGE')
- DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
- dbcc PAGE ( {'dbname' | dbid}, filenum, pagenum [, printopt={0|1|2|3} ])
- DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系
- 0:输出可读形式的数据页页头数据
- 1:输出可读形式的数据页页头数据,并且还有槽位对应记录的十六进制内容
- 2:输出可读形式的数据页页头数据,输出整个数据页页头的十六进制数据,整一页的内容都显示,包括未使用的空间。
- 3:输出可读形式的数据页页头数据,并且包括记录中每个字段的可读形式,行溢出数据也会显示数据内容,但是大对象则不显示内容,而是说明其存储位置!所以选项3,也是输出内容最全面的。
3.2.2 测试案例
- DBCC TRACEON(3604)
dbcc page('dbpage',1,309,0)- ----------------------------------------------------------------------------------------------------------
- PAGE: (1:309)
- BUFFER:
- BUF @0x000000027C0827C0
- bpage = 0x000000026FA86000 bhash = 0x0000000000000000 bpageno = (1:309)
- bdbid = 10 breferences = 0 bcputicks = 0
- bsampleCount = 0 bUse1 = 46781 bstat = 0xb
- blog = 0x212121cc bnext = 0x0000000000000000
- PAGE HEADER:
- Page @0x000000026FA86000
- m_pageId = (1:309) m_headerVersion = 1 m_type = 1
- /*
- m_pageId 当前页面号码;m_headerVersion 版本号,始终为1;m_type 页面数据类型,1为数据页面,10为IAM页面等,具体参考pagetype
- */
- m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0xc000
- /*
- m_typeFlagBits 数据页和索引页为4,其他页为0
- m_level 该页在索引页(B树)中的级数,0表示为叶子节点
- m_flagBits 页面标志
- */
- m_objId (AllocUnitId.idObj) = 35 m_indexId (AllocUnitId.idInd) = 256
- /*
- m_indexId (AllocUnitId.idInd) 索引ID,0 代表堆, 1 代表聚集索引, 2-250 代表非聚集索引 大于250就是text或image字段
- */
- Metadata: AllocUnitId = 72057594040221696
- Metadata: PartitionId = 72057594038976512 Metadata: IndexId = 1
- Metadata: ObjectId = 341576255 m_prevPage = (0:0) m_nextPage = (0:0)
- /*
- Metadata: AllocUnitId 存储单元的ID,sys.allocation_units.allocation_unit_id
- Metadata: PartitionId 数据页所在的分区号,sys.partitions.partition_id
- Metadata: ObjectId 该页面所属的对象的id,sys.objects.object_id
- Metadata: IndexId sys.objects.object_id&sys.indexes.index_id
- m_prevPage 该数据页的前一页面
- m_nextPage 该数据页的后一页面
- */
- pminlen = 8 m_slotCnt = 1 m_freeCnt = 5035
- m_freeData = 3155 m_reservedCnt = 0 m_lsn = (39:400:68)
- m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
- m_tornBits = 0 DB Frag ID = 1
- /*
- pminlen 定长数据所占的字节数为多少个字节
- m_slotCnt 页面中的数据的行数
- m_freeCnt 页面中剩余的空间,还剩多少字节的空间
- m_freeData 页面空闲空间的起始位置,一个页面8KB约等于8192字节 页面空闲空间的位置在3155
- m_reservedCnt 活动事务释放的字节数
- m_lsn 日志记录号
- m_xactReserved 最新加入到m_reservedCnt领域的字节数
- m_xdesId 添加到m_reservedCnt的最近的事务id
- m_ghostRecCnt 幻影数据的行数
- m_tornBits 页的校验位或者被由数据库页面保护形式决定页面保护位取代
- 数据库页面的 lsn
- SQL Server在内存中维护一个哈希表,记录下自己所有做过写入动作的页面最新的LSN(Log Sequence Number)值。
- 在下次读出页面的时候,会去比较这两个值是否相等。由于LSN是个自动增长的唯一值,每个发生新修改的页面,
- LSN的值会比原来的要大。所以如果读到的LSN与内存中存放的不一致,就说明上次的写入请求没有真正完成。
- 这时824错误也会被触发。
- */
- Allocation Status
- GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED
- PFS (1:1) = 0x60 MIXED_EXT ALLOCATED 0_PCT_FULL DIFF (1:6) = CHANGED
- ML (1:7) = NOT MIN_LOGGED
- dbcc page('dbpage',1,309,1)
- ----------------------------------------------------------------------------------------------------------
- 页头信息省略中...
- Slot 0, Offset 0x60, Length 3059, DumpStyle BYTE
- /*
- Slot 槽位号,一个槽位一行数据,这一行数据从 0x60 = 96开始,长度是 3059 bytes
- 下文文该行记录的16进制内容
- */
- Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
- Record Size = 3059
- Memory Dump @0x000000001F978060
- /*下文为这一行记录 3059个字节内容*/
- 0000000000000000: 30000800 01000000 0400a003 002b80e3 0bf38b02 0............+......
- 0000000000000014: 00000001 000000d5 69000070 17000033 01000001 ........i..p...3....
- 0000000000000028: 00000028 72707429 2c594553 204f5220 4e4f2c53 ...(rpt),YES OR NO,S
- 000000000000003C: 59535245 4d4f5445 4c4f4749 4e532054 59504553 YSREMOTELOGINS TYPES
- 中间省略...
- 0000000000000BCC: 7072696d 61727920 6b65792c 616e7369 5f6e756c primary key,ansi_nul
- 0000000000000BE0: 6c5f6400 00d10700 00000031 01000001 000100 l_d........1.......
- OFFSET TABLE:
- Row - Offset
- 0 (0x0) - 96 (0x60)
- 选项为2, 输出整个数据页页头的十六进制数据,整一页的内容都显示,包括未使用的空间。
- dbcc page('dbpage',1,309,2)
- ----------------------------------------------------------------------------------------------------------
- 页头信息省略中...
- /*下文为一整页的数据存储情况,包括行记录跟空闲空间,不区分槽位*/
- DATA:
- Memory Dump @0x0000000028178000
- 0000000028178000: 01010000 00c00001 00000000 00000800 00000000 ....................
- 0000000028178014: 00000100 23000000 ab13530c 35010000 01000000 ....#.....S.5.......
- 0000000028178028: 27000000 90010000 44000000 00000000 00000000 '.......D...........
- 000000002817803C: 00000000 01000000 00000000 00000000 00000000 ....................
- 0000000028178050: 00000000 00000000 00000000 00000000 30000800 ................0...
- 0000000028178064: 01000000 0400a003 002b80e3 0bf38b02 00000001 .........+..........
- 0000000028178078: 000000d5 69000070 17000033 01000001 00000028 ....i..p...3.......(
- 000000002817808C: 72707429 2c594553 204f5220 4e4f2c53 59535245 rpt),YES OR NO,SYSRE
- 00000000281780A0: 4d4f5445 4c4f4749 4e532054 59504553 2c535953 MOTELOGINS TYPES,SYS
- 省略中...
- 0000000028178C1C: 65726963 20726f75 6e646162 6f72742c 7072696d eric roundabort,prim
- 0000000028178C30: 61727920 6b65792c 616e7369 5f6e756c 6c5f6400 ary key,ansi_null_d.
- 0000000028178C44: 00d10700 00000031 01000001 00010000 00212121 .......1.........!!!
- 0000000028178C58: 21212121 21212121 21212121 21212121 21212121 !!!!!!!!!!!!!!!!!!!!
- 0000000028178C6C: 21212121 21212121 21212121 21212121 21212121 !!!!!!!!!!!!!!!!!!!!
- 省略中...
- 0000000028179FE0: 21212121 21212121 21212121 21212121 21212121 !!!!!!!!!!!!!!!!!!!!
- 0000000028179FF4: 21212121 21212121 21216000 !!!!!!!!!!`.
- OFFSET TABLE:
- Row - Offset
- 0 (0x0) - 96 (0x60)
选项为3, 输出可读形式的数据页页头数据,并且包括记录中每个字段的可读形式,行溢出数据也会显示数据内容,但是大对象则不显示内容,而是说明其存储位置!
- dbcc page('dbpage',1,309,3)
- -------------------------------------------------------------------------------------------------
- 页头信息省略中...
- Slot 0 Offset 0x60 Length 3059
- /*
- Slot 槽位号,一个槽位一行数据,这一行数据从 0x60 = 96开始,长度是 3059 bytes
- */
- Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
- Record Size = 3059
- Memory Dump @0x000000002BB78060
- /*下文为这一行记录 3059个字节内容*/
- 0000000000000000: 30000800 01000000 0400a003 002b80e3 0bf38b02 0............+......
- 0000000000000014: 00000001 000000d5 69000070 17000033 01000001 ........i..p...3....
- 0000000000000028: 00000028 72707429 2c594553 204f5220 4e4f2c53 ...(rpt),YES OR NO,S
- 000000000000003C: 59535245 4d4f5445 4c4f4749 4e532054 59504553 YSREMOTELOGINS TYPES
- 0000000000000050: 2c535953 52454d4f 54454c4f 47494e53 20545950 ,SYSREMOTELOGINS TYP
- 0000000000000064: 45532028 55504441 5445292c 41463a20 61676772 ES (UPDATE),AF: aggr
- 0000000000000078: 65676174 65206675 6e637469 6f6e2c41 503a2061 egate function,AP: a
- 中间省略...
- 0000000000000BB8: 2c6e756d 65726963 20726f75 6e646162 6f72742c ,numeric roundabort,
- 0000000000000BCC: 7072696d 61727920 6b65792c 616e7369 5f6e756c primary key,ansi_nul
- 0000000000000BE0: 6c5f6400 00d10700 00000031 01000001 000100 l_d........1.......
- /*下文为 在槽位0 slot 0 的 这一行记录 ,详细描述每一列的存储情况*/
- Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4
- /*slot 0,第一列 在本页占用4字节,列名为id,值为1*/
- id = 1
- namea = [BLOB Inline Root] Slot 0 Column 2 Offset 0x13 Length 24 Length (physical) 24
- /*slot 0,第2列,本页占用24字节,列名为namea
- 这里可以看到是发生了行溢出情况,列中没有数据,但是存储了该列的实际位置
- 实际大小为6000字节,值 存储在第一个文件第307页的 slot 0 槽位上*/
- Level = 0 Unused = 0 UpdateSeq = 1
- TimeStamp = 1775566848 Type = 2
- Link 0
- Size = 6000 RowId = (1:307:0)
- Slot 0 Column 3 Offset 0x2b Length 3000 Length (physical) 3000
- nameb = (rpt),YES OR NO,SYSREMOTELOGINS TYPES,SYSREMOTELOGINS TYPES (UPDATE),AF: aggregate function,AP: applicati
- on,C : check cns,...省略中...primary key,ansi_null_d
- /*slot 0,第3列 ,本页占用3000字节,列名为nameb ,值为 nameb= 的后面一大段中*/
- descriptions = [Textpointer] Slot 0 Column 4 Offset 0xbe3 Length 16 Length (physical) 16
- /*slot 0,第4列 ,该列为text数据类型,本页占用16字节,列名为descriptions,其值存储在第一个文件的第305页的 slot 1 槽位上*/
- TextTimeStamp = 131137536 RowId = (1:305:1)
- Slot 0 Offset 0x0 Length 0 Length (physical) 0
- /*该表格有主键 ,该行的keyhashvalue值*/
- KeyHashValue = (8194443284a0)
- create table tbpage(id int primary key not null identity(1,1) ,cola int,colb varchar(10),colc varchar(100))
- insert into tbpage(cola,colb,colc) select object_id,type,name from sys.objects
- create index ix_colc on tbpage(colc)
- select * from sys.indexes where name='ix_colc'
- dbcc ind('dbpage','tbpage',-1)

《SQL Server性能调优实战》
SQL SERVER大话存储结构(1)_数据页类型及页面指令分析的更多相关文章
- SQL SERVER大话存储结构(3)_数据行的行结构
一行数据是如何来存储的呢? 变长列与定长列,NULL与NOT NULL,实际是如何整理存放到 8k的数据页上呢? 对表格进行增减列,修改长度,添加默认值等DDL SQL ...
- SQL SERVER大话存储结构(6)_数据库数据文件
数据库文件有两大类:数据文件跟日志文件,每一个数据库至少各有一个数据文件或者日志文件,数据文件用来存储数据,日志文件用来存储数据库的事务修改情况,可用于恢复数据库使用. 这里分 ...
- SQL SERVER大话存储结构(4)_复合索引与包含索引
索引这块从存储结构来分,有2大类,聚集索引和非聚集索引,而非聚集索引在堆表或者在聚集索引表都会对其 键值有所影响,这块可以详细查看本系列第二篇文章:SQL SERVER大话存储结构 ...
- SQL SERVER大话存储结构(5)_SQL SERVER 事务日志解析
本系列上一篇博文链接:SQL SERVER大话存储结构(4)_复合索引与包含索引 1 基本介绍 每个数据库都具有事务日志,用于记录所有事物以及每个事物对数据库所作的操作. 日志的记录 ...
- SQL SERVER大话存储结构(2)_非聚集索引如何查找到行记录
如果转载,请注明博文来源: www.cnblogs.com/xinysu/ ,版权归 博客园 苏家小萝卜 所有.望各位支持! 1 行记录如何存储 这里引入两个 ...
- Oracle和sql server中复制表结构和表数据的sql语句
在Oracle和sql server中,如何从一个已知的旧表,来复制新生成一个新的表,如果要复制旧表结构和表数据,对应的sql语句该如何写呢?刚好阿堂这两天用到了,就顺便把它收集汇总一下,供朋友们参考 ...
- [转帖]真TM长的:SQL Server 2008存储结构——GAM和SGAM、PFS结构、IAM结构、DCM&BCM
谈到GAM和SGAM,我们不得不从数据库的页和区说起. https://blog.csdn.net/snowfoxmonitor/article/details/49991015 一个数据库由用户定义 ...
- SQL Server复制表结构和表数据生成新表的语句
参考:http://topic.csdn.net/t/20020621/09/820025.html SELECT * INTO newTableName FROM oldTabl ...
- 使用Spark加载数据到SQL Server列存储表
原文地址https://devblogs.microsoft.com/azure-sql/partitioning-on-spark-fast-loading-clustered-columnstor ...
随机推荐
- VMware10虚拟机安装mac os x 10.9
身边越来越多盆友们购进mac本子,不断地诱惑着小编,近日一直想着买一台mac本子装逼,后来想想用来开发(非ios)可能会遇着各种问题,就先在虚拟机上玩玩看,毕竟1,2万RMB不是小数. 在mac os ...
- otool是mac自带的工具
找了一晚上otool的安装包,到最后才发现mac自带otool
- js 判断是否为空对象、空数组
当需要判断参数是否为空时,总希望 js 能够提供原生的判断方法,可惜并没有,只能自己封装了. function isEmpty(obj) { // 检验 undefined 和 null if(!ob ...
- NIO(三、Channel)
目录 NIO(一.概述) NIO(二.Buffer) NIO(三.Channel) Channel 上文说了描述了Buffer的实现机制,那么这个章节就主要描述数据是如何进入缓冲区的,并且又是如何从缓 ...
- Windows7 java-jdk1.7安装及设置变量过程
jdk安装的次数较少,容易忘记,这里专门记录一下. 1:jdk1.7网上到处都是可以随便下载一个.然后进行安装,不过在安装过程中把安装路径单独记忆一下,在配置变量的时候会用到. 2:安装完JDK后配置 ...
- shell中的特殊变量和函数传参
shell中的特殊变量 $? :上一个命令的执行状态返回值 $#::参数的个数 $*:参数列表,所有的变量作为一个字符串 $@:参数列表,每个变量作为单个字符串 $1-9,${10}:位置参数 $$: ...
- JAVA循环依赖
最近看到一个问题:如果有两个类A和B,A类中有一个B的对象b,B类中有一个A的对象a,那么如何解决这两个类的相互依赖关系呢? 示例代码1 public class Test{ public stati ...
- Struts2的类型转换(上)
传统的类型转换.(略) Struts2中提供了内置的基本的类型转换器,可以实现基本类型的自动转换,包括: Integer, Float, Double, Decimal Date and Dateti ...
- Gson解析复杂的Bean类实现Parcelable
import java.util.ArrayList; import android.os.Parcel; import android.os.Parcelable; import android.s ...
- ASP.NET Core 网站发布到Linux服务器
长期以来,使用.NET开发的应用只能运行在Windows平台上面,而目前国内蓬勃发展的互联网公司由于成本的考虑,大量使用免费的Linux平台,这就使得.NET空有一身绝技但无法得到广大的施展空间,.N ...