如何降低SQL Server中的HEAP碎片?


10

我最近发现,一个堆表的碎片超过70%。所以我决定做一个

ALTER TABLE dbo.myTable REBUILD

有趣的是,事后我有20%的支离破碎。从那以后,在那张桌子上没有写任何东西。因此,我决定再进行一次重建。

第二次后,桌子上的帽子碎裂了50%,甚至更多! 我真的不明白怎么会这样...


你说逻辑上的碎片是什么意思。就数据页的使用而言,这是碎片。我知道没有顺序,但是无序数据本身并不是零散的。在这种情况下,碎片意味着有效使用数据页。
tuxmania's

2
我猜我们应该问,桌子多大?在行和页中。
科迪·科尼奥尔

Answers:


17

碎片在堆中意味着什么

avg_fragmentation_in_percent通过查询sys.dm_db_index_physical_statsDMV 从列中获得的堆中的碎片值指出:

索引的逻辑碎片,或IN_ROW_DATA分配单元中的的扩展碎片

此外,同一BOL表示

这是堆的叶子页面中乱序扩展的百分比。乱序范围是指包含堆的当前页面的范围实际上不是包含上一页的范围之后的下一个范围的范围。

因此,您可以看到,分配给堆的页面不是存在的可用空间,而是造成碎片的页面顺序变化

这可以通过小测试来证明。让我们创建一个堆表并在其中插入一些记录,然后检查碎片。

create table dbo.HeapTest
(
Id INT not NULL Default (1),
Col1   char(5000) Not null Default ('Heaps Are Cool')
)

SET NOCOUNT ON

Insert into dbo.Heaptest default values
go 50

select index_type_desc,avg_fragmentation_in_percent,fragment_count,
avg_page_space_used_in_percent,record_count
from sys.dm_db_index_physical_stats(db_id(),object_id('dbo.HeapTest','U'),0,default,'detailed')

因此,将在其中创建带有50条记录的堆表。以下是查询DMV sys.dm_db_index_physical统计信息后的碎片情况

在此处输入图片说明

您可以看到avg_fragmentation_in_percent列值为33%。现在让我们看看页面的排列方式。这可以通过使用未记录的查询来完成%%lockres%%。该查询将是

SELECT  %%lockres%%, * FROM dbo.HeapTest;

下面是输出的样子。仅附上它的相关部分。由于我们在dbo.HeapTest表中插入了50行,因此该查询产生了50行。

在此处输入图片说明

它说的是第一页具有ID 197,下一页具有ID,242随后的页面具有连续的ID,直到我们到达页面ID为止,264因为在此之后我们得到页面ID 280。因此,页面ID号的这种跳跃实际上是导致碎片的原因。

现在,以免重建堆并再次运行该命令以查看碎片和页面排列方式。我们会像

在此处输入图片说明

您可以看到碎片化了14%

让我们看看分配的页码

在此处输入图片说明

我们只有一个跳转点,所有页面都按顺序分配了页面ID。由于仅一跳碎片就大大减少了。

我再次重建了堆,现在当我检查碎片时,它完全消失了。和页面ID分配就像

在此处输入图片说明

为什么碎片增加

现在,关于可能导致碎片增加的原因,我们可以证实这一事实:当页面被分配给堆时,它们将不是连续的,正如您所见,导致碎片增加的原因是分配给页面的PAGE ID的跳跃。

在脑后,您还应记住,HEAP的碎片一词没有任何含义,您将如何为一堆无序页面定义碎片。

真的担心碎片

如果确实遇到堆表碎片化且查询变慢的情况,那么在表上创建聚集索引比重建它更好。原因是当您重建堆时,所有基础非索引索引也会被重建,从而导致重建过程花费更长的时间,这会占用大量资源并膨胀事务日志。在生产系统上,人们总是会尝试避免这种情况。保罗在他关于堆的神话部分对此进行了论述

PS:请不要在生产系统上使用未记录的命令。这只是为了演示。


感谢您的详细分析。我面临着大堆表,因为一些数据保险库爱好者认为它比使用聚簇索引要好得多,但是随后他们在这些堆上使用了大量的检查约束和非聚簇索引,因此在这种情况下,我真的看不到堆的好处。但是,由于我只是愚蠢的开发人员,所以我不得不处理这个问题。再次感谢您的见解:)
tuxmania's

您如何从sys.dm_db_index_physical_stats(db_id(),object_id('dbo.HeapTest','U')仅从结果返回0的默认值上选择select index_type_desc,avg_fragmentation_in_percent,fragment_count,avg_page_space_used_in_percent,record_count一张桌子?即使我正确地在'object_id'中指定了表名,它也会为我返回所有表的所有索引
Mickael

@Mickael我使用了函数db_id()来获取当前数据库,并且我特别指定了对象名称,因此它将始终查看当前数据库并寻找Heaptest并给出结果。我确定您可能错过了一些东西。只要确保兼容性级别不是80,在这种情况下db_id函数就无法正常工作
-Shanky

@Shanky为什么不建议在生产中使用无证查询%% lockres %%?您能详细解释一下吗?
拉夫

@ user1624552仅仅因为它没有文档,所以MS也不保留有关它的文档的更新。它的后效应是什么,它如何工作,在任何地方都没有记录,这就是为什么要这样做的原因。例如,有一个命令fn_dump_dblog()会创建隐藏的调度程序,这不好。此命令也不受支持。您可以使用它,但风险在于您。
Shanky
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.