SQL Server 数据库页的大小定义为 8192 字节。有一些标头信息据说大小为 96 字节。
如果您曾经尝试创建一个包含超过 8053 字节的列定义的表,那么您将遇到错误消息:
Creating or altering table 'Generated_Data_GUID' failed because the minimum row size would be 8061, including 7 bytes of internal overhead. This exceeds the maximum allowable table row size of 8060 bytes.
以下是一个示例表 DDL:
CREATE TABLE [dbo].[Generated_Data_GUID](
[ID] [int] IDENTITY(1,1) NOT NULL,
[GUID] [uniqueidentifier] NOT NULL,
[SEQGUID] [uniqueidentifier] NOT NULL,
[Data1] [char](4000) NULL,
[Data2] [char](4000) NULL,
[Data3] [char](9) NULL,
[EntryDate] [datetime2](7) NULL
) ON [PRIMARY]
使用上面的 DDL,如果我将列的列定义更改为Data3
,char(10)
那么我将遇到错误消息。
每种列类型的字节大小如下:
int : 4 bytes
uniqueidentifiere : 16 bytes
char(n) : n bytes
datetime2(n) : 6 bytes if n < 3
7 bytes if n = 3 or n = 4
8 bytes if n > 4
如果我们做一些简单的数学运算,那么我们最终会得到以下计算:
Page Size : 8192 bytes
-----------
Header : 96 bytes -
Internal Overhead : 7 bytes -
Max Size : 8053 bytes -
-----------
Missing Data : 36 bytes
===========
问题
这 36 个字节包含什么?
参考资料
- 页面和范围架构指南(Microsoft 文档)
- 存储引擎内部:页面剖析(SQLSkills.com)
Paul Randal 实际上在您链接到的博客文章的评论中回答了这个确切的问题:
因此,在回答您帖子正文中的问题时:
页中“额外”的 36 个字节使用如下:
只是为了确认问题中定义的表实际上是 8060 字节宽,让我们进行完整的重现。
首先,我们将设置数据库和表,并向其中插入一行。我正在添加聚集索引,因为堆是最糟糕的。
通过运行以下 DBCC 命令,我们可以看到所有分配了索引的页面:
页面类型为 1 的页面是索引页面(页面 ID 336)。我们可以使用其他 DBCC 命令转储有关该页面的各种信息:
以下是该命令输出的一些重要片段。从标题部分:
这意味着页面上有 34 个字节的可用空间。这是您在原始帖子中概述的 36,减去插槽数组条目的 2 个字节。说到其中:
这意味着此页面上只有一条记录。
现在,在记录部分:
这表明存储在此页面上的单个记录为 8060 字节(这是所有数据类型存储大小的总和加上每条记录 7 字节的开销)。
所以我们在这个页面上确实有一个全尺寸的 8060 字节记录。然而,如果我们更努力地尝试,我们仍然可以将额外的 34 个字节挤到这个页面上。
例如,我可以创建一个 2015 字节宽的表。每行将占用页面中的 2015 + 7(内部开销)+ 2(槽数组)= 2024 字节。所以四行加起来应该是 8096 字节,恰好填满了 96 字节标头之后剩下的空间。让我们在同一个数据库中尝试一下:
现在我们找到我们的页面,并且只有一个页面:
所以现在我们想获取第 352 页的信息:
这是好东西:
没有可用空间!这个页面充满了我们的 4 行。