SQL如何决定添加新数据页面

Ram*_*ale 5 sql sql-server

我试图理解SQL如何在我们将一些新记录插入表中时分配新数据页.

我在新的数据库中创建了一个名为Employee的示例表 -

Create Database TestDb
GO
Use TestDb
GO
Create table Employee (
EmployeeName char(1000))   GO
Insert into Employee values ('Employee1')

DBCC IND('TestDb','dbo.Employee',-1)
Run Code Online (Sandbox Code Playgroud)

运行DBCC后,我看到了2页.一个是IAM页面(PageType = 10),另一个是数据页面(PageType = 1),它保存实际数据.

后来我使用DBCC Page验证了数据页面的实际内容

DBCC TRACEON(3604)
DBCC PAGE('TestDb',1,298,3)
Run Code Online (Sandbox Code Playgroud)

我看到SQL如何计算m_freecnt字节数 -

= 1007字节RecordSize + 96字节标头+ 2字节偏移= 1105字节

即8K页= 8192字节= 8192 - 1105 = 7087字节自由.

在此输入图像描述

现在我继续在此表中添加记录,以便了解此页面将容纳多少条记录,以及SQL何时会分配一个保留m_FreeCnt的新页面.

(记录大小= 1007和偏移字节= 2)

新增第二条记录 -

Insert into Employee values ('Employee2') GO
Run Code Online (Sandbox Code Playgroud)

最后一个自由计数= 7087,即7087 - 1007 - 2 = 6087 => m_FreeCnt = 6078

Insert into Employee values ('Employee3') GO
Run Code Online (Sandbox Code Playgroud)

最后一个自由计数= 6078,即6078 - 1007 - 2 = 5069 => m_FreeCnt = 5069

Insert into Employee values ('Employee4') GO
Run Code Online (Sandbox Code Playgroud)

最后一个自由计数= 5069即5069 - 1007 - 2 = 4060 => m_FreeCnt = 4060

Insert into Employee values ('Employee5') GO
Run Code Online (Sandbox Code Playgroud)

最后一个自由计数= 4060,即4060 - 1007 - 2 = 3051 => m_FreeCnt = 3051

Insert into Employee values ('Employee6') GO
Run Code Online (Sandbox Code Playgroud)

最后一个自由计数= 3051,即3051 - 1007 - 2 = 2042 => m_FreeCnt = 2042

Insert into Employee values ('Employee7') GO
Run Code Online (Sandbox Code Playgroud)

最后一个自由计数= 2042,即2042 - 1007 - 2 = 1033 => m_FreeCnt = 1033

直到现在一切正常.现在我们在此数据页面中剩下1033个字节.一旦我添加第8条记录,理想情况下它不应该创建另一页,因为可用的字节数是1033字节,这足以容纳第8条记录(1009字节就足够了).但是,SQL会创建一个新的"日期"页面来保存此第8条记录.

我插入第8条记录并运行DBCC IND来检查 -

Insert into Employee values ('Employee7') GO

DBCC IND('TestDb','dbo.Employee',-1)
Run Code Online (Sandbox Code Playgroud)

现在它已经创建了一个PageNumber = 300的新数据页面.

我不明白这一部分.SQL是否保留了一些保留的字节,除了[header(96)+ Data part + offset per page 2 bytes]这个?

您可以尝试运行以上查询并让我知道如果我在这里错过任何东西?或者我们是否应该不关心SQL的这些内存细节?

谢谢.

小智 2

只需重新表述另一个答案即可明确:
一旦您达到页面填充的 81%,该页面的 PFS 记录将把前 2 位更改为11,这意味着“页面已满 95%”并且 SQL 将即使有足够的空间(超过 5% 可用),也不允许插入更多记录。

有两种方法可以解决这个问题:
简单:创建聚集索引 - 然后您可以根据需要将填充因子设置为 100%。
困难:如果页面上的可用空间超过 19%,则可以使用 ONEINSERT语句插入多条记录以 100% 填充页面。
参见示例:

CREATE TABLE TestTable(F0 SMALLINT);
GO
INSERT INTO TestTable (F0) 
SELECT TOP 699 1 FROM sys.messages;
GO
BEGIN TRAN 
  INSERT INTO TestTable (F0) 
  SELECT TOP 37 2 FROM sys.messages;
  SELECT 
    (SELECT COUNT(*) FROM sys.dm_db_database_page_allocations(DB_ID(), OBJECT_ID('TestTable'), NULL, NULL, 'DETAILED') WHERE Page_type = 1) AS Pages,
    (SELECT COUNT(*) FROM TestTable) AS "Count";
ROLLBACK
GO
BEGIN TRAN 
  INSERT INTO TestTable (F0) VALUES (3)
  GO 2
  SELECT 
    (SELECT COUNT(*) FROM sys.dm_db_database_page_allocations(DB_ID(), OBJECT_ID('TestTable'), NULL, NULL, 'DETAILED') WHERE Page_type = 1) AS Pages,
    (SELECT COUNT(*) FROM TestTable) AS "Count";
ROLLBACK
GO
DROP TABLE TestTable
GO
Run Code Online (Sandbox Code Playgroud)

达到 699 条记录后,您可以一次插入 37 条新记录,如果逐条记录插入,则只能插入一条记录。