在聚集索引上重建,为什么datasize缩小?


10

当我们在其中包含约15gb数据的表的聚集索引上进行重建时,数据大小缩小到5gb,这怎么可能?什么样的“数据”被删除?

数据大小,我的意思是DBCC sp_spaceused的“数据”列

在聚集索引上重建之前:

name                  rows        reserved    data        index_size  unused
LEDGERJOURNALTRANS    43583730    39169656 KB 15857960 KB 22916496 KB 395200 KB

在聚集索引上重建后:

name                  rows        reserved    data        index_size  unused
LEDGERJOURNALTRANS    43583730    29076736 KB 5867048 KB  22880144 KB 329544 KB

用于重建的TSQL:

USE [DAX5TEST]
GO
ALTER INDEX [I_212RECID] ON [dbo].[LEDGERJOURNALTRANS] REBUILD PARTITION = ALL WITH ( PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, ONLINE = ON, SORT_IN_TEMPDB = OFF, DATA_COMPRESSION = PAGE, FILLFACTOR = 85 )
GO

您是从文件大小确定数据大小吗?
JNK 2014年

数据大小我的意思是“数据” DBCC列注释sp_spaceused
丹尼尔·比约克

那将是的“数据”列EXEC sp_spaceused
RLF 2014年

1
是否每个人都错过了OP在他的重建脚本中使用了页面压缩=启用的功能,我想这不是以前的事情了。丹尼尔可以确认吗?
Shanky

1
@Shanky:该ALTER INDEX语句看起来像是由代码生成的(因为它的默认设置中包含一堆选项),因此我怀疑它是根据索引的现有选项构建的。但是您是正确的:如果在运行索引之前对群集索引启用压缩那么这肯定可以解释大部分数据占用量的减少。(再次:丹尼尔,您能确认一种方法还是另一种方法?)
David Spillett 2014年

Answers:


16

当一个表具有聚集索引时,该索引就是表数据(否则您具有堆类型表)。重建聚集索引(实际上是任何索引,但对于非聚集索引而言,该空间不会被视为“数据”)将导致部分使用的页面合并为更完整的形式。

当您按照索引顺序将数据插入索引(集群或其他)时,将根据需要创建叶页,并且您将永远只有一个分页:最后一个分页。当您输入的数据不按索引顺序排列时,需要对页面进行拆分以使数据适合在正确的位置:您最终得到了两个页面,这些页面大约一半已满,新行进入其中一个页面。随着时间的流逝,这可能会发生很多,消耗大量的额外空间,尽管将来的插入将在某种程度上填补一些空白。非叶子页面也会看到类似的效果,但是实际数据页面的大小远比它们重要。

此外,删除可能会导致页面分页。如果删除页面中的所有行,则将其视为“未使用”,但如果剩余一行或多行数据,仍将被视为正在使用。即使一页中只有一行使用10个字节,该页在已用空间计数中也算为8192字节。同样,将来的插入可能会填补一些空白。

对于可变长度的行,更新也可以具有相同的效果:随着行的变小,它可能会在其页面中留出空间,以后不易重用;如果几乎满页面的行变长,则可能会导致页面拆分。

SQL Server不会花时间尝试通过重新安排页面的使用方式来规范化数据,直到明确告知诸如索引重建顺序之类的信息为止,因为此类垃圾回收活动可能是性能方面的噩梦。

我怀疑这是您所看到的,尽管我会说分配足够的空间来存储大约2.7倍于数据绝对需要的空间是一个特别糟糕的情况。这可能暗示您有一些随机的东西作为索引中的重要键之一(也许是UUID列),这意味着不太可能以索引顺序添加新行,并且/或者最近发生了大量删除操作。

页面拆分示例

按索引顺序插入固定长度的行,其中四行适合页面:

Start with one empty page: 
        [__|__|__|__]
Add the first item in index order:
        [00|__|__|__]
Add the next three
        [00|02|04|06]
Adding the next will result in a new page:
        [00|02|04|06] [08|__|__|__]
And so on...
        [00|02|04|06] [08|10|12|14] [16|18|__|__]

现在要以不按索引顺序添加行(这就是为什么我仅在上面使用偶数的原因):添加11将意味着要么扩展第二页(不可能,因为它们的大小是固定的),就将11以上的所有内容都向上移动一个(太昂贵了)大索引)或按以下方式拆分页面:

[00|02|04|06] [08|10|11|__] [12|14|__|__] [16|18|__|__]

从这里开始,添加1317不会导致拆分,因为相关页面中当前有空间:

[00|02|04|06] [08|10|11|__] [12|13|14|__] [16|17|18|__]

但是添加03将:

[00|02|03|__] [04|06|__|__] [08|10|11|__] [12|13|14|__] [16|17|18|__]

如您所见,在执行这些插入操作之后,我们目前已分配了5个数据页,总共可以容纳20行,但是那里只有14行(“浪费”了30%的空间)。

使用默认选项进行重建(请参阅下面的“填充因子”)将导致:

[00|02|03|04] [06|08|10|11] [12|13|14|16] [17|18|__|__]

在这个简单的示例中保存一页。可以很容易地看出删除操作如何能与乱序插入产生类似的效果。

减轻

如果期望数据相对于索引顺序而言是相当随机的顺序,则FILLFACTOR可以在创建或重建索引时使用该选项,以告诉SQL Server人为地留出空白以供以后填充-从长远来看减少页面拆分,但是最初占用更多空间。当然,弄错了这个值可能会使事情变得更糟,而不是使情况变得更好,因此请谨慎处理。

页面拆分(特别是在聚集索引上)可能会对插入/更新产生性能影响,因此FILLFACTOR有时会对此进行调整,而不是进行大量写入活动的数据库中的空间使用问题(但对于大多数应用而言,读取胜于写入)在几个数量级上,通常最好将填充因子保持在100%,除非是特殊情况(例如您对具有有效随机内容的列进行索引)。

我假设其他大型数据库也有类似的选择,如果您也需要这种级别的控制。

更新资料

关于ALTER INDEX在我开始键入以上内容后添加到问题的语句:我假设选项与首次构建(或最后重建)索引时相同,但如果不相同,则如果添加了压缩选项,则压缩选项可能非常重要时间。同样在该语句中,填充因子设置为85%而不是100%,因此,每个叶子页在重建后将立即变为约15%的空白。


2
+1如果页面填充因子小于100%,例如,如果页面填充因子为50%,则新重建的聚集索引()将是使用100%填充因子重建时的两倍。
Max Vernon 2014年

6

当您重建索引时,它实际上会将所有数据放在新页面上。我怀疑发生的事是您在重建之前删除了很多数据,例如,删除了列,更新了可变宽度列以减少数据,更改了固定宽度列大小或删除了很多行。这些操作中的任何一个都可能在页面上留下很多空白,只有在重建之前这些空白才能被回收。其中的“数据”列sp_spaceused不是在测量实际数据,而是用于存储数据的8K页数。由于重建,这些页面现在更满了,因此相同数量的数据适合较少的页面。


5

sp_spaceused存储过程不检查数据库中行的总累积大小。它以分配给数据的扩展数据块的累积大小报告正在分配的用于保存该数据的空间大小。

如果有大量可用空间(例如,从许多已删除的行中删除),则出于性能原因,重新构建聚簇索引将压缩页面和扩展区中的空间,使其效率更高(即更小)。

因此,不应丢弃任何数据,但是重建过程使嵌入在数据页中的可用空间再次可用。

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.