确定 SHRINKDATABASE 将从哪些对象中释放空间

Dre*_*rew 5 sql-server shrink disk-space fragmentation

首先,是的,收缩是不好的。永远不要使用它。我明白。不幸的是,这是一种 SOP,我无法争论,因为它通常是权宜之计,直到我们可以获得一个维护窗口来分配更多空间并重建/重组索引。

我们最近遇到了一个问题,我们从传统备份切换到第三方工具。由于数据库的大小和我们最近删除的一些大对象,在转换之前使用新工具进行干净备份之前缩小它是有意义的。我有一些保留意见,但我们继续这样做,因为它很少引起问题。显然,清除日志并不是真正的问题,但我们正在缩小 mdf。这导致了对少数大型、高度事务性对象的大量阻塞,并且正确地提出了一些关于我们如何使用它的非常严重的问题。

我已经做了很多研究,但还没有找到足够让我满意的细节。

  • 是否有任何地方可以实际描述 SHRINK 操作的内部结构?

  • 有没有办法确定哪些物体比其他物体更碎片化以及它如何释放空间?或者,与表中的其他表相比,表的碎片化程度是否无关紧要,并且更多的是关于对象的整体大小?

保罗兰德尔在这里谈了一点,但还不够详细。

我知道您可以基于 dm 索引统计数据进行排序,但是由于大小和过度索引,在我们的一些较大的数据库上运行非常困难。我也不确定这是否就是我需要关心的全部。主表存储为堆,只有索引真正碎片化,但我不确定不同的分区如何影响事物,或者底层数据库的其他部分是否也可以被释放。

我确实也看到了这个问题,但我不确定我是否能够将它配置为我们正在寻找的那种粒度。

Bra*_*adC 5

一些想法:

1.从不使用SHRINKDATABASE,总是使用SHRINKFILE

您希望控制它从哪个文件占用空间,以及到底占用多少空间,以便保留一些空间。运行如下查询以查看每个文件中的实际已用/可用空间:

SELECT DB_NAME() as dbname, type_desc, name as logical_name, 
    CONVERT(decimal(12,1),size/128.0) as TotalMB,
    CONVERT(decimal(12,1),FILEPROPERTY(name,'SpaceUsed')/128.0) as UsedMB,
    CONVERT(decimal(12,1),(size - FILEPROPERTY(name,'SpaceUsed'))/128.0) as FreeMB,
    physical_name
FROM sys.database_files WITH (NOLOCK)
ORDER BY type, file_id;
Run Code Online (Sandbox Code Playgroud)

假设您的主要数据文件为 100GB,但实际仅使用了 20GB,您的最终目标大小可能为 25GB 或 30GB,以留出一些内部可用空间。所以你会做类似的收缩:

DBCC SHRINKFILE (name='logical_name', size=30000)
Run Code Online (Sandbox Code Playgroud)

2. 内部SHRINKFILE行为

在内部,收缩数据文件只是将使用过的页面从文件末尾一次移动到开头附近的一个开放位置,直到文件末尾有足够的空间来满足您的目标大小。

这是一种相当简单的暴力方法,并且不关注页面的内容,这就是它经常导致表碎片的原因。从您链接的保罗·兰德尔文章中:

数据文件收缩操作一次对单个文件进行,并使用 GAM 位图来查找文件中分配的最高页。然后它会尽可能地将其移向文件的前面,依此类推。

如果它想要移动的页面被另一个进程锁定,它将被阻塞。

3. 收缩者保持进步

如果您开始收缩,然后由于阻塞或其他冲突活动而必须停止它,它将保留其进度。成功移动的所有页面都将保持移动状态。您可以稍后继续完成工作。

4. 找出哪些表会受到收缩的影响

实际上可能有一些方法可以做到这一点,使用对sys.allocation_unitsor 的查询sys.partitions,或者可能使用DBCC PAGEor 的东西,但我的建议是:不要打扰,大型数据库将在数据文件中分散数千个对象的剩余部分,其中大部分是您可以使用的。不在乎。

如果您坚持的话,这里是我通过搜索找到的一些高级文章:

当它开始阻塞时,您已经可以找出哪些表/索引在起作用,其他一切都是噪音。

如果该锁永远不会清除以使您有机会移动页面,那么您可能必须安排一些没有其他活动的停机时间。

5. 完成后,重新索引以修复碎片

数据文件可能会再次增长一点,但除非您的数据库是一个大型表,否则您仍然可能得到比开始时更小的最终结果。

  • @DrewCopenhaver 当然可以。Paul 的文章末尾还有一个想法可能值得考虑:1) 创建一个新文件组 2) 使用“CREATE INDEX ...WITH (DROP_EXISTING=ON)”移动所有(或选定的)表/索引 3)删除或缩小旧文件组中剩余的内容。这肯定需要对这些表/索引进行停机,但会为您留下从文件开头放置的新的原始表(没有碎片,没有空白空间)。 (2认同)