A SQL Server database page is defined as to be 8192 bytes in size.
There is some header information which is said to be 96 bytes in size.
And if you have ever tried to create a table with more than 8053 bytes of column definitions then you will hit the error message:
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.
Following is an example table 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]
With the above DDL if I change the column definition for column Data3
to be char(10)
, then I will hit the error message.
The byte sizes for each column type are as follows:
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
If we do some simple maths, then we end up with the following calculation:
Page Size : 8192 bytes
-----------
Header : 96 bytes -
Internal Overhead : 7 bytes -
Max Size : 8053 bytes -
-----------
Missing Data : 36 bytes
===========
Question
What do these 36 bytes contain?
Reference Material
- Pages and Extents Architecture Guide (Microsoft Docs)
- Inside the Storage Engine: Anatomy of a page (SQLSkills.com)
Best Answer
Paul Randal actually answers this exact question in the comments on the blog post you linked to:
So, in answer to the question in the body of your post:
The "extra" 36 bytes in a page are used as follows:
Just to confirm that the table defined in the question is, in fact, 8060 bytes wide, let's have a full repro.
First we'll set up the database and the table, and insert one row into it. I'm adding a clustered index because heaps are the worst.
We can see all the pages allocated the index by running this DBCC command:
The one with a page type of 1 is the index page (page ID 336). We can dump all kinds of information about that page with this other DBCC command:
Here are some important snippets from the output of that command. From the HEADER section:
This means there are 34 bytes of free space on the page. That's the 36 you outlined in your original post, minus the 2 bytes for the slot array entry. Speaking of which:
This means there is only one record on this page.
Now, down in the record section:
This indicates that the single record stored on this page is 8060 bytes (which is the sum of all the data type storage sizes plus the 7 bytes per-record overhead).
So we do have a fully sized, 8060 byte record on this page. However, we can still squeeze an additional 34 bytes onto this page if we try harder.
For instance, I could create a table that's 2015 bytes wide. Each row would then take up 2015 + 7 (internal overhead) + 2 (slot array) = 2024 bytes in the page. So four rows should add up to 8096 bytes, exactly filling the space left over after the 96 byte header. Let's try it in the same database:
Now we find our page, and there is only one as expected:
So now we want to get info on page 352:
And here's the good stuff:
No free space! This page is slap full with our 4 rows.