说理论,总是枯燥的,先来段搞笑视频,清清脑 模特兒走秀摔倒集錦: 原文地址: 继续存储引擎揭秘系列,今天讨论页结构。页是用来存储记录的。一个页是数据库文件中的一个 8192 字节段。页在数据文件中开始于 0 字节,并按 8192 字节对齐。下面是一个页的基
说理论,总是枯燥的,先来段搞笑视频,清清脑
模特兒走秀摔倒集錦:
原文地址:
继续存储引擎揭秘系列,今天讨论页结构。页是用来存储记录的。一个页是数据库文件中的一个 8192 字节段。页在数据文件中开始于 0 字节,并按 8192 字节对齐。下面是一个页的基本结构图:

页面分成个部分:BUFFER、PAGEHEADER、DATA、OFFSET TABLE
BUFFER:显示了指定页面的缓冲信息。由于它是一个内存中结构,所以仅当页面处于内存中时候才有效.
PAGEHEADER:显示指定页面的所有报头字段信息。
DATA:显示每行数据的具体存储.
OFFSET TABLE:显示了所有行偏移矩阵的内容.
页头部
页头部大小为
96
字节。在这部分我最想做的事是使用
DBCC PAGE
来看一个页头部,然后解释一下所有的字段含义。我使用以前《
page split
》文章用的数据库,下面是
DBBC PAGE
部分输出:
DBCC
TRACEON (3604)
DBCC
PAGE ('pagesplittest', 1, 143, 1);
GO
m_pageId
= (1:143) m_headerVersion = 1
m_type = 1
m_typeFlagBits = 0x4 m_level =
0
m_flagBits = 0x200
m_objId (AllocUnitId.idObj) = 68 m_indexId
(AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594042384384
Metadata: PartitionId =
72057594038386688 Metadata: IndexId =
1
Metadata: ObjectId = 2073058421 m_prevPage = (0:0) m_nextPage =
(1:154)
pminlen = 8
m_slotCnt = 4 m_freeCnt = 4420
m_freeData = 4681
m_reservedCnt = 0
m_lsn = (18:116:25)
m_xactReserved = 0 m_xdesId = (0:0)
m_ghostRecCnt = 0
m_tornBits = 1333613242
下面是所有字段的解释(注意页中字段并不是按下面顺序存储排列的):
m_pageId
这个字段标明了文件
ID
及页在该文件中的位置。在本例中(
1:143
)
m_headerVersion
页头部版本。自从
7.0
以来,这个值总是为
1
。
m_type
页类型,你可能见到的页类型如下:
1 -
数据页。这种页存储堆或聚集索引的叶节点中的数据记录。
2 -
索引页。这种页存储聚集索引的非叶节点
或非聚集索引的所有结点
的索引记录
3 -
文本混合页。这种文本页存储小段的
LOB
值以及文本树的内部。这种页可被同一索引
/
堆的分区的
LOB
值所共享。
4 -
文本树页。这种文本页存储一个单独列的大段的
LOB
值。
7 -
排序页。这种页存储在一次排序操作中的中间结果。
8 - GAM
页。这种页存有一个
GAM
区间(每个数据文件逻辑上被分割成约
4GB
大小的段,这个“约
4GB
”就是一个页中的位图所能表示的区)中所有区的分配信息:一个区是否已经被分配?
GAM
表示全局分配映射(
G
lobal A
llocation M
ap
)。第一个
GAM
页是每个文件的第
2
页。
9 -
SGAM
页。这种页也是存有一个
GAM
区间中所有区的分配情况:一个区是否可以分配混合页?
SGAM
表示共享
GAM
。第一个
SGAM
页是每个文件的第
3
页。
10 – IAM
页。这种页包含一个
GAM
区间中哪些区已分配给一个索引(
SQL
SERVER 2000
中)或分配单元(
2005
中)。
IAM
表示索引分配映射(
Index Allocation Map
)。
11 - PFS
页。这种页存有一个
PFS
区间(每个数据文件逻辑上被分割成约
64MB
大小的段,这个“约
64MB
”就是一个页中的字节所能表示的页)中每个的页的分配和可用空间的信息。
PFS
表示页的可用空间。第一个
PFS
页是每个文件的第
1
页。
13 -
启动页。这种页含有数据库的信息。每个数据库只有一个启动页,它是数据文件
1
的第
9
页。
15 -
文件头页。这种页包含文件的信息。每个文件一个文件头页,是文件的第
0
页。
16 -
差异映射页
(DIFF)
。这种页包含有自上次完整备份以来一个
GAM
区间中已发生改变的区的信息。第一个
DIFF
页是每个文件的第
6
页。
17 - ML
映射页。这种页包含有自上次备份以来一个
GAM
区间中哪些区在
BULK-LOGGED
模式下发生了大容量日志操作。你为了大容量加载或重建索引而将恢复模式变为
BULK-LOGGED
,有了这种页就不用担心打断备份链了。第一个
ML
页是每个文件的第
7
页。
m_typeFlagBits
基本未用。数据页和索引页,此字段总是
4
;其他类型页(除了
PFS
页)该字段总是为
0
。如果一个
PFS
页的
m_typeFlagBits
为
1
,表示
PFS
页映射的
PFS
区间中的至少有一页中有至少一个幽灵记录。
m_level
这表示页在
B
树上的层。
叶节点是
0
层,每向上加一层增加
1
,直到根节点(即
B
树的最高点)。
在
SQL
SERVER 2000
中,一个聚集索引的叶节点(数据页)是
0
,它的上面一层(索引页)也是
0
,,然后才向上增加,直到根节点。所以在
SQL SERVER 2000
中为了判断一个页是否是叶节点,你需要查看
m_type
和
m_level
两个字段。
除了索引页外所有其他类型的页,其层次总是为
0
。
m_flagBits
这包含了一些用来描述页的不同的标志。比如,
0x200
表示页上面有校验和(就像我们的例子);
0x100
表示页上面有残损页保护。
一些位在
SQL
SERVER 2005
中不再使用。
m_objId
m_indexId
在
SQL
SERVER 2000
中,这些
ID
表示本页所分配给的实际的关系对象和索引的
ID
。在
SQL SERVER 2005
中,不再是这样了。分配元数据全部改了,所以这些字段不再表示
ID
了而是表示本页所属的分配单元了。
m_prevPage
m_nextPage
这是
B
树上同一层中的前一页和后一页的指针。这些字段都是
6
个字节的页
ID.
索引的每层上的页都用一个双向链表按索引的逻辑顺序(就是定义的索引键)链接起来。因为存在碎片,所以指针指向的页没有必要跟当前页物理上相邻。
B
树上一层最左面的页的
m_prevpage
为
NULL;
最右面的页的
m_nextpage
为
NULL.
堆或者只有一页的索引中,所有页的这两个指针都是
NULL.
pminlen
页中记录的定长部分的大小。
m_slotCnt
页中记录的个数。
m_freeCnt
页中有多少字节的可用空间。
m_freeData
从页开始到最后一个
记录结尾的下一字节的偏移值。如果它前面也有可用空间也没有关系。
m_reservedCnt
由活动事务保留的可用空间的字节数。这可以防止用光所有的可用空间,以保证事务能正确回滚。改变这个值有一套复杂的算法。
m_lsn
最后一次修改本页的日志的
LSN.
m_xactReserved
最后一次加到
m_reservedCnt
上的数目。
m_xdesId
最近一次加到
m_reserverdCnt
上的事务的内部
ID.
m_ghostRecCnt
页中幽灵记录的数目。
m_tornBits
本字段或者是页的校验和或者是残损页保护中被替换的位。这是依赖于本数据库到底是用哪种保护方式。