Alp*_*mum 13 index sql-server data-pages t-sql
我创建了下表:
CREATE TABLE dbo.TestStructure
(
id INT NOT NULL,
filler1 CHAR(36) NOT NULL,
filler2 CHAR(216) NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
然后创建了一个聚集索引:
CREATE CLUSTERED INDEX idx_cl_id
ON dbo.TestStructure(id);
Run Code Online (Sandbox Code Playgroud)
接下来我用 30 行填充它,每个大小为 256 字节(基于表声明):
DECLARE @i AS int = 0;
WHILE @i < 30
BEGIN
SET @i = @i + 1;
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (@i, 'a', 'b');
END;
Run Code Online (Sandbox Code Playgroud)
现在根据我在“Training Kit (Exam 70-461): Querying Microsoft SQL Server 2012 (Itzik Ben-Gan)”一书中读到的信息:
SQL Server 在内部以页为单位组织数据文件中的数据。一个页面是一个 8 KB 的单位,属于一个单一的对象;例如,到表或索引。页是读取和写入的最小单位。页面被进一步组织成范围。一个区由八个连续的页面组成。一个范围内的页面可以属于单个对象或多个对象。如果页面属于多个对象,则该范围称为混合范围;如果页面属于单个对象,则该范围称为统一范围。SQL Server 将对象的前八页存储在混合区中。当对象超过八页时,SQL Server 会为此对象分配额外的统一区。通过这种组织,小物体浪费的空间更少,大物体的碎片化程度也更低。
所以这里我有第一个混合范围 8KB 页面,填充了 7680 字节(我插入了 30 次 256 字节大小的行,所以 30 * 256 = 7680),检查大小我已经运行 size check proc - 它返回以下结果
index_type_desc: CLUSTERED INDEX
index_depth: 1
index_level: 0
page_count: 1
record_count: 30
avg_page_space_used_in_percent: 98.1961947121324
name : TestStructure
rows : 30
reserved : 16 KB
data : 8 KB
index_size : 8 KB
unused : 0 KB
Run Code Online (Sandbox Code Playgroud)
因此为表保留了 16 KB,第一个 8 KB 页面用于 Root IAM 页面,第二个用于叶数据存储页面,即 8KB,占用约 7.5 KB,现在当我插入一个 256 字节的新行时:
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (1, 'a', 'b');
Run Code Online (Sandbox Code Playgroud)
尽管它有 256 字节的空间(7680 b + 256 = 7936 仍然小于 8KB),但它没有存储在同一页中,创建了一个新的数据页,但该新行可以放在同一个旧页上,为什么 SQL Server 会在可以节省空间和搜索时间的情况下创建一个新页面,而是将其插入到现有页面中?
注意:堆索引中也发生了同样的事情。
Sol*_*zky 11
虽然 SQL Server 确实使用 8k(8192 字节)数据页来存储 1 行或更多行,但每个数据页都有一些开销(96 字节),而每一行都有一些开销(至少 9 字节)。8192 字节不是纯粹的数据。
有关其工作原理的更详细检查,请参阅我对以下 DBA.SE 问题的回答:
与 sys.allocation_units 中的表大小不匹配的 DATALENGTH 总和
使用该链接答案中的信息,我们可以更清楚地了解实际行大小:
UsingDBCC PAGE
通过显示:(Record Size 263
对于tempdb
)和Record Size 277
(对于设置为 的数据库)来确认我的计算ALLOW_SNAPSHOT_ISOLATION ON
。
现在,有 30 行,即:
没有版本信息
30 * 263 会给我们 7890 字节。然后为使用的 7986 字节添加页头的 96 字节。最后,添加槽数组的 60 个字节(每行 2 个),页面上总共使用了 8046 个字节,剩余 146 个。使用DBCC PAGE
通过显示来确认我的计算:
m_slotCnt 30
(即行数)m_freeCnt 146
(即页面上剩余的字节数)m_freeData 7986
(即数据 + 页头 -- 7890 + 96 -- 槽数组不计入“已用”字节计算)有版本信息
30 * 277 字节共 8310 字节。但是 8310 超过了 8192,这甚至没有考虑 96 字节的页头,也没有考虑每行 2 字节的插槽数组(30 * 2 = 60 字节),这应该只为我们提供 8036 个可用字节的行。
但是,29 行呢?这将为我们提供 8033 字节的数据 (29 * 277) + 96 字节的页头 + 58 字节的槽数组 (29 * 2) 等于 8187 字节。这将使页面剩余 5 个字节(8192 - 8187;当然不可用)。使用DBCC PAGE
通过显示来确认我的计算:
m_slotCnt 29
(即行数)m_freeCnt 5
(即页面上剩余的字节数)m_freeData 8129
(即数据 + 页头 -- 8033 + 96 -- 槽数组不计入“已用”字节计算)关于堆
堆填充数据页的方式略有不同。他们对页面上剩余的空间量进行了非常粗略的估计。当在DBCC输出看,看一行:PAGE HEADER: Allocation Status PFS (1:1)
。你会看到VALUE
沿的线条显示的东西0x60 MIXED_EXT ALLOCATED 0_PCT_FULL
(我看了一下聚集表),或0x64 MIXED_EXT ALLOCATED 100_PCT_FULL
在看堆表时。这是按事务评估的,因此执行单独的插入(例如此处执行的测试)可能会显示集群表和堆表之间的不同结果。但是,对所有 30 行执行单个 DML 操作将按预期填充堆。
然而,这些关于堆的细节都没有直接影响这个特定的测试,因为表的两个版本都适合 30 行,只剩下 146 个字节。无论是集群还是堆,这都不足以容纳另一行。
请记住,这个测试相当简单。计算行的实际大小可能会变得非常复杂,具体取决于各种因素,例如:SPARSE
、数据压缩、LOB 数据等。
要查看数据页面的详细信息,请使用以下查询:
DECLARE @PageID INT,
@FileID INT,
@DatabaseID SMALLINT = DB_ID();
SELECT @FileID = alloc.[allocated_page_file_id],
@PageID = alloc.[allocated_page_page_id]
FROM sys.dm_db_database_page_allocations(@DatabaseID,
OBJECT_ID(N'dbo.TestStructure'), 1, NULL, 'DETAILED') alloc
WHERE alloc.[previous_page_page_id] IS NULL -- first data page
AND alloc.[page_type] = 1; -- DATA_PAGE
DBCC PAGE(@DatabaseID, @FileID, @PageID, 3) WITH TABLERESULTS;
Run Code Online (Sandbox Code Playgroud)
**如果您的数据库设置为ALLOW_SNAPSHOT_ISOLATION ON
或 ,则将出现 14 字节的“版本信息”值READ_COMMITTED_SNAPSHOT ON
。
小智 9
您的数据行不是 256 字节。每一个更像是 263 个字节。由于 SQL Server 中数据行的结构,纯固定长度数据类型的数据行具有额外的开销。查看此站点并了解数据行的构成方式。 http://aboutsqlserver.com/2013/10/15/sql-server-storage-engine-data-pages-and-data-rows/
因此,在您的示例中,您有一个 256 字节的数据行,为状态位添加 2 个字节,为列数添加 2 个字节,为数据长度添加 2 个字节,为空位图添加 1 个左右的字节。即 263 * 30 = 7,890 字节。添加另一个 263 并且您超过了 8kb 页面限制,这将强制创建另一个页面。
归档时间: |
|
查看次数: |
912 次 |
最近记录: |