SQL如何决定添加新数据页

时间:2016-08-08 13:54:28

标签: 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)

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

后来我使用DBCC Page

验证了数据页面的实际内容
DBCC TRACEON(3604)
DBCC PAGE('TestDb',1,298,3)

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

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

即。 8K Page = 8192 bytes = 8192 - 1105 = 7087 Bytes Free。

enter image description here

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

  

(记录大小= 1007& Offset Bytes = 2)

添加了第二条记录 -

Insert into Employee values ('Employee2') GO

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

Insert into Employee values ('Employee3') GO

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

Insert into Employee values ('Employee4') GO

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

Insert into Employee values ('Employee5') GO

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

Insert into Employee values ('Employee6') GO

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

Insert into Employee values ('Employee7') GO

最后一个自由计数= 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)

现在它已经创建了一个具有PageNumber = 300的新数据页。

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

您可以尝试运行以上查询并告诉我如果我在这里遗漏了任何内容?或者我们是否应该不关心SQL的这些内存细节?

感谢。

2 个答案:

答案 0 :(得分:2)

重新说出另一个答案,说清楚:
一旦达到填充页面的81%,该页面的PFS记录将首先将2位更改为11,这意味着“页面已满95%”,SQL将不允许再插入任何记录即使有足够的空间(超过5%是免费的)。

有两种方法可以解决这个问题:
简单:创建聚集索引 - 然后,如果需要,您将能够将fillfactor设置为100% 困难:如果您的页面上有超过19%的可用空间,您可以使用一个INSERT语句插入多个记录,以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

在您达到699条记录后,您可以一次插入37条新记录,或者只按一条记录插入一条记录。

答案 1 :(得分:0)

这就是我发现的:http://social.technet.microsoft.com/wiki/contents/articles/21877.sql-server-how-does-sql-server-allocate-space-in-a-heap.aspx

  

总共有2条记录存储在数据页面(m_slotCnt)和   该页面有3,063个字节可用于新记录(m_freeCnt)。如果   Microsoft SQL Server将在此页面中插入新记录   插入偏移5,126(m_freeData)。最后但并非最不重要的   该页面被使用了51% - 80%。如果Microsoft SQL Server会读取   来自页眉的信息应该知道3,062字节   页面上的可用空间。但事实并非如此!

     

记录3的长度为2504字节+行开销但Microsoft   由于SQL Server没有在页面150上插入记录   以下计算:

     

PFS报告空间使用率为80%,这意味着6,554字节   完整的页面空间。页面的可用空间是1,638字节和   记录3的长度为2,504字节,不适合页面   基于这些计算!该计算表明了PFS   扫描而不是页眉的专用扫描。因为新的   记录不适合页面,将创建一个新的记录   从事务日志中提取将显示。

对于你的例子:

m_slotCnt   7
m_freeCnt   1033
m_freeData  7145
PFS (1:1)   0x63 MIXED_EXT ALLOCATED  95_PCT_FULL

8192 * 0.95 = 7782

8192-7782 = 409 <1007

我重复了你的例子,行长度= 10个字符(107字节)和我得到的结果:

m_freeCnt   357
PFS (1:1)   0x64 MIXED_EXT ALLOCATED 100_PCT_FULL

实际PFS = 95.6%,但SQL引擎将其计为100_PCT_FULL,并为下一行创建新页面