具有高度碎片化的堆

Ton*_*raj 4 sql-server heap sql-server-2012 index-maintenance

堆上的高碎片会影响查询性能吗?

我可以重建堆ALTER TABLE REBUILD以消除碎片吗?

这是用于报告的临时表。它确实有非聚集索引,但没有聚集索引。

Joh*_*ner 6

多年来,我已经看到这个问题出现了足够多的时间,我认为它最终需要一个真正的答案。是的,这个答案是事后多年,但现在的事实与 2015 年一样。

堆碎片是一个红鲱鱼,坦率地说我不会太关注它。老实说,我说的是一般的碎片化,充其量是未来问题的一个指标,对性能的真正损害是每页过多的空白。既然这不是你的问题,我不会再离题了,只是将你指向Brent Ozar 的一篇文章,如果你想进入那个兔子洞,它会进一步刺激你的胃口。

对于堆,您需要关注的是转发记录/转发指针计数。 引用Kalen Delaney 关于该主题的一篇更好的帖子

当堆中的一行必须移动时,SQL Server 会留下一个转发指针来代替已移动的行

SQL Server 跳出顺序并跟随每个转发记录的转发指针。

这是实际的碎片,因为它引入了额外的读取,因此很重要,因此根据您的堆中有多少转发记录,这可能代表每当您将堆读入内存时需要执行的大量 I/O。

如何找出堆中存在多少转发记录指针?您只需对有问题的堆运行以下查询:

SELECT    index_type_desc
        , page_count
        , avg_page_space_used_in_percent
        , avg_fragmentation_in_percent
        , avg_record_size_in_bytes
        , forwarded_record_count, 1.0*(forwarded_record_count)/(1.0*(page_count)) AS forwarded_record_pct
FROM    sys.dm_db_index_physical_stats(db_id(), object_id('schema.tablename'),null, null, 'detailed')
WHERE   index_type_desc = 'HEAP'
GO
Run Code Online (Sandbox Code Playgroud)

以下是供应商提供的应用程序数据库中一些实际表的结果,我目前支持:

结果表 1: 在此处输入图片说明

结果表 2: 在此处输入图片说明

正如您所看到的,两个表中的碎片实际上相当高,但我不会浪费我的维护时间来重建任何一个表,因为还没有足够的转发记录来关注它。我的理由是,堆只会通过表扫描操作或RID 查找(在堆上定义的非聚集索引未覆盖的情况下)才被带入内存。当执行表扫描时,整个表被加载到内存中,此时碎片几乎没有那么重要。如果可能的话,我想在一般情况下避免表扫描,但是当它们发生时,我希望它们尽可能高效,因此减少转发指针直接有助于实现这一点。 RID 查找也受前向指针的影响,因此即使是针对具有 RID 查找的 NCI 的查找操作,如果遇到转发的记录,也会受到额外 I/O 的影响。当我达到堆中转发指针的某个阈值时,我将重建表。该阈值根据相关表而变化,但我的一般规则是,与总页数相比,一旦堆具有 10% 或更多的转发记录页,我就希望重建堆。这个数据库中的大多数堆都不会受到转发记录的影响,所以我不必太担心,但对于你的情况来说不一定如此。

此外,在某些情况下,堆可能无法正确释放空页基本上,如果您发现针对堆表的工作负载主要不是插入驱动的,或者您发现经常发生转发记录,您将需要研究使用聚簇索引对表进行聚簇。