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