堆上的压缩

14 sql-server heap compression

以下是Microsoft Docs 中的一段:

在堆重建之前,作为 DML 操作的一部分在堆中分配的新页面不会使用 PAGE 压缩。通过移除和重新应用压缩,或者通过创建和移除聚集索引来重建堆。

我不明白为什么会这样。如果我有一个具有指定压缩设置的堆,为什么不将它应用于属于该表的页面?

谢谢

Sol*_*zky 12

虽然我不知道导致差异的具体内部机制,但我可以说堆的管理(内部)与聚集索引(也可能是非聚集索引)略有不同:

  • 从堆中删除行使得一个或多个数据页为空(没有分配的行)不一定释放该空间。您可能需要在表上创建然后删除聚集索引,或者调用ALTER TABLE [TableName] REBUILD;(从 SQL Server 2014 开始?)。有关更多详细信息和选项,请参阅DELETE的 Microsoft Docs 页面。

  • 插入单个行(即不是基于集合的INSERT)插入堆中不会像使用聚集索引那样完全填充数据页。只要有行空间(数据和行开销)加上槽数组的 2 字节开销,聚集索引就会适合行。然而,Heaps 中的数据页不使用页面上剩余的字节数,而是使用一个非常概括的指示页面有多满的指标,并且报告的级别并不多。级别大致为:0%、20%、50%、80% 和 100% 满。并且它将切换到 100%,同时还有另一行的空间(事实上,如果在基于集合的操作中插入相同数量的行,那么它会尽可能地填满页面)。当然,就像DELETE 操作,重建堆将打包尽可能多的行,以适应数据页。

现在考虑从 Microsoft Docs 页面的“页面压缩发生时”部分获取的以下信息,用于页面压缩实现

... 当数据添加到第一个数据页时,数据被行压缩。... 当页面已满时,要添加的下一行启动页面压缩操作。整个页面都经过审核;...

因此,这似乎与其他堆行为完全一致,即在写入数据页之前,它们需要 ALTER TABLE REBUILD、CREATE / DROP 的聚集索引或更改数据压缩设置(所有这些都重建堆)最佳。如果 Heaps 不完全知道“整个页面”(直到 Heap 重建)并且不知道页面何时肯定已满,那么它们将不知道何时启动页面压缩操作(在处理更新和单页时) - 行插入)。

另一个进一步限制某些堆自动应用页面压缩(即使它们可以)的技术是应用压缩将需要重建该堆的所有非聚集索引(如果存在)。正如“数据压缩”的链接页面也指出:

更改堆的压缩设置需要重建表上的所有非聚集索引,以便它们具有指向堆中新行位置的指针。

所指的“指针”是行 ID (RID),它们是以下各项的组合:文件 ID、页面 ID 和页面上的槽/位置。这些 RID 被复制到非聚集索引中。作为一个精确的物理位置,它们有时比使用聚集索引键遍历 b 树更快。但是,物理位置的一个缺点是它可以改变,这就是这里的问题。然而,聚集索引不会遇到这个问题,因为它们的键值被复制到非聚集索引中作为返回聚集索引的指针。并且键值保持不变,即使它们的物理位置发生变化。

另见:

  • 堆(没有聚集索引的表)的 Microsoft Docs 页面的“管理堆”部分:

    要重建堆以回收浪费的空间,请在堆上创建聚集索引,然后删除该聚集索引。

  • Microsoft Docs数据压缩页面的“使用行和页面压缩时的注意事项”部分:

    当堆配置为页面级压缩时,页面仅通过以下方式接收页面级压缩:

    • 在启用批量优化的情况下批量导入数据。
    • 数据是使用 INSERT INTO ... WITH (TABLOCK) 语法插入的,并且表没有非聚集索引。
    • 通过执行带有 PAGE 压缩选项的 ALTER TABLE ... REBUILD 语句重建表。

    以及问题中引用的声明。