我正在尝试理解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。
现在我继续向此表添加记录,以便了解此页面将容纳多少条记录,以及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的这些内存细节?
感谢。
答案 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)
总共有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,并为下一行创建新页面