以下是Microsoft Docs的一段:
在DML操作中分配给堆中的新页面在重建堆之前不会使用PAGE压缩。通过删除和重新应用压缩,或通过创建和删除聚簇索引来重建堆。
我不知道为什么会这样。如果我的堆具有指定的压缩设置,为什么不将其应用于属于该表的页面?
谢谢
以下是Microsoft Docs的一段:
在DML操作中分配给堆中的新页面在重建堆之前不会使用PAGE压缩。通过删除和重新应用压缩,或通过创建和删除聚簇索引来重建堆。
我不知道为什么会这样。如果我的堆具有指定的压缩设置,为什么不将其应用于属于该表的页面?
谢谢
Answers:
虽然我不知道造成这种差异的具体内部机制,但我可以说,堆的管理(内部)与聚簇索引(可能还包括非聚簇索引)略有不同:
从堆中删除行以使一个或多个数据页为空(没有分配的行)不一定会释放该空间。您可能需要在表上创建然后删除聚簇索引,或者进行调用ALTER TABLE [TableName] REBUILD;
(从SQL Server 2014开始)。有关更多详细信息和选项,请参阅Microsoft文档页面以进行DELETE。
将单个行(即不是基于集合的行INSERT
)插入到堆中不会像使用聚集索引那样完全填充数据页。只要该行有空间(数据和行开销)加上插槽数组的2字节开销,聚集索引将适合行。但是,堆中的数据页不使用页面上剩余的字节数,而是使用非常笼统的指示符来指示页面的填充程度,并且报告的层数并不多。级别大致为:0%,20%,50%,80%和100%充满。并且它将切换到100%,同时还有另一行的空间(实际上,如果在基于集合的操作中插入了相同数量的行,则它将尽可能多地填满页面)。当然,就像DELETE
操作,重建堆将打包到数据页面上的尽可能多的行。
现在考虑以下信息,取自“当页面压缩发生”为微软文档页面的部分页面压缩实现:
...当数据添加到第一个数据页时,数据将进行行压缩。...当页面已满时,要添加的下一行将启动页面压缩操作。整个页面都经过审查;...
因此,这似乎完全符合此其他堆行为,即在写入数据页之前,它们将需要ALTER TABLE REBUILD,CREATE / DROP聚集索引或更改Data Compression设置(所有这些都重建堆)。最佳。如果堆不完全了解“整个页面”(直到重建堆)并且不知道页面何时肯定已满,那么他们将不知道何时启动页面压缩操作(在处理更新和单个操作时)。 -行插入)。
另一个会进一步限制某些堆无法自动应用页面压缩的技术(即使可能的话)是,应用压缩将需要重建该堆的所有非聚集索引(如果存在)。正如该链接的“数据压缩”页面还指出:
更改堆的压缩设置需要重建表上的所有非聚集索引,以便它们具有指向堆中新行位置的指针。
所指的“指针”是行ID(RID),它们是以下各项的组合:FileID,PageID和页面上的插槽/位置。这些RID被复制到非聚簇索引中。作为精确的物理位置,与使用聚簇索引键遍历b树相比,它们有时查找速度更快。但是,物理位置的一个缺点是它可以更改,这就是这里的问题。但是,聚簇索引不会遇到此问题,因为它们的键值作为指向聚簇索引的指针被复制到非聚簇索引中。而且,即使键值的物理位置发生变化,键值也保持不变。
另请参阅:
Microsoft文档页的“管理堆”部分中的堆(不带聚集索引的表):
要重建堆以回收浪费的空间,请在堆上创建一个聚集索引,然后删除该聚集索引。
Microsoft Docs页面用于数据压缩的 “使用行和页面压缩时的注意事项”部分:
当将堆配置为页面级压缩时,页面仅通过以下方式获得页面级压缩:
- 启用批量优化后,批量导入数据。
- 使用INSERT INTO ... WITH(TABLOCK)语法插入数据,并且该表没有非聚集索引。
- 通过使用PAGE压缩选项执行ALTER TABLE ... REBUILD语句来重建表。
问题中引用的陈述。
并非SQL Server中的每种机制都像我们认为的那样。
保罗·兰德尔(Paul Randal)就此问题提供了强有力的建议。
http://www.sqlskills.com/blogs/paul/a-sql-server-dba-myth-a-day-2930-fixing-heap-fragmentation/