我知道收缩是魔鬼:它颠倒页面顺序,并导致皮肤癌、数据碎片化和全球变暖。问题还在继续... 话虽如此,假设我有一个100 GB的数据库,我删除了50 GB的数据——不是在一个表上,而是在数据库整体层面上对旧数据进行了普遍修剪,涵盖了90%的表——这是否构成了适合收缩数据库的用例?
如果不是,从数据库中删除如此高比例的数据后,应采取哪些适当的步骤来清理空间?我可以想到两个步骤:重建索引和更新统计信息。还有其他什么?
我知道收缩是魔鬼:它颠倒页面顺序,并导致皮肤癌、数据碎片化和全球变暖。问题还在继续... 话虽如此,假设我有一个100 GB的数据库,我删除了50 GB的数据——不是在一个表上,而是在数据库整体层面上对旧数据进行了普遍修剪,涵盖了90%的表——这是否构成了适合收缩数据库的用例?
如果不是,从数据库中删除如此高比例的数据后,应采取哪些适当的步骤来清理空间?我可以想到两个步骤:重建索引和更新统计信息。还有其他什么?
重新组织和缩小是不被推荐的。
如果你可以将数据库正在服务的应用程序下线,你可以在收缩之前移除所有非聚集索引(这可能意味着删除依赖非聚集索引的外键约束),以加快过程并减少索引碎片化(这将意味着要移动的数据较少,因为只有数据页会被重新排序,而现在不存在的索引页则不会被重新排序,从而加快过程),然后重新创建所有索引和键。
在收缩之后重新创建索引意味着它们不应该出现显著的碎片,而在收缩期间没有索引意味着重建它们不会在文件中留下许多小的"空洞",这可能会导致以后的碎片化。
如果你可以将应用程序下线,另一个选择是将所有数据迁移到具有相同结构的新数据库。如果你的构建过程可靠,你应该能够快速构建空白的数据库;否则,可以从当前数据库创建一个(还原当前数据库的备份,清空/删除表中的所有内容,并执行完整收缩)。
你可能还想在目标数据库中删除所有索引,并在之后重新创建它们,因为当改变大量索引数据时(在这种情况下是100%的数据),这样做会更高效。为了加快复制过程的速度,将目标数据库的数据文件放在与源数据库不同的物理驱动器上(除非您使用SSD,那么您就不需要关心减少磁头移动),完成后可以将它们移回源位置。 另外,如果将目标数据库创建为新数据库(而不是通过清空源数据库的副本),请使用一个初始大小,足以容纳当前所有数据以及几个月的增长量 - 这样做将使数据复制过程再次加快,因为它不会在整个过程中不断分配新空间。 这可能比使用收缩操作更好,因为将数据迁移到全新的数据库会复制收缩操作的预期效果,但可能会产生更少的碎片化(这是重新组织和收缩的意外后果)。收缩操作只是从文件末尾附近取出块并将其放置在靠近开头的第一个空间中,没有努力保持相关数据在一起。 我怀疑结果在空间方面也会更有效率,因为之后可能会有更少的部分使用页面。缩小尺寸只是将部分使用页面移动,而将数据移动更有可能导致完整页面,特别是如果按表的聚集键/索引(如果表有)的顺序插入到目标位置,并在数据全部迁移后创建其他索引。 另一个比迁移到新数据库更现场化的选择是创建一个新的文件组,并将所有数据迁移到其中,如https://stackoverflow.com/a/2438060/114292中所讨论的那样 - 一旦所有数据和非聚集索引都被移动,基本文件组的文件可以缩小到非常小的尺寸,因为它们几乎为空,而新的文件组应该在空间上相当有组织。 当然,如果您无法完全离线运行应用程序,仅执行收缩操作是您唯一的选择,所以如果您真的需要回收空间,请选择这种方式。根据您的数据、访问模式、常见工作集大小、服务器的RAM量等因素,最后额外的内部碎片可能并不那么重要。 对于复制操作,无论是使用SSIS还是基本的T-SQL都可以(SSIS选项可能效率较低,但后期维护可能更容易)。如果您在最后创建了外键关系和索引,无论哪种情况下,都可以简单地进行“对每个表进行复制”。当然,对于一次性操作,收缩+重组也可能是可以接受的,但我只是喜欢吓唬人们,让他们永远不要考虑定期收缩!(我知道有些人每天都安排这样的操作)。 对于将数据复制到新数据库以有效地收缩和重组数据的进一步选择,在最初提出这个问题时,这在除了新兴的Azure SQL之外并不常见,即将整个数据库导出为bacpac文件并进行恢复。恢复操作会创建数据库架构,批量加载数据,然后重新创建非聚集索引。这种方法可能比上述方法花费更长的时间,但需要您进行的手动干预或脚本编写较少,因此您可以更加确信它会正常工作。更新至2023年:随着现代存储系统主要基于固态设备,并具有大量用于进一步缓存的RAM(存储设备上的缓存,SQL本身中的缓冲池),页面位置方面的碎片化问题已经不再是一个很大的关注点,这可能改变了建议的平衡。关于拥有很多部分使用的页面方面的碎片化仍然需要考虑(您会使用更多的缓冲池来存储相同的数据,并且需要更多的IO来读取或更新相同数量的数据),只能通过索引重建而非重新组织来修复,同样适用于由于删除数据而产生大量空闲空间(这是该问题中的原始问题)。在此背景下,现在更容易推荐进行一次性收缩。
如果你的空间不足,并且数据不应该变得那么大,那么可以进行缩小操作,但在此之后要重建索引,并使用合适的填充因子以适应典型的增长。
如果你的最终目标是减小备份文件的大小,请确保实施全面的备份策略来清除事务日志,并在备份数据库时使用压缩选项。
我不建议自动增长5GB,除非你经常需要增长5GB。否则,你可能会遇到间歇性的性能问题。你应该首先将数据大小设置为你认为在一年内所需的大小,并将自动增长设置为一个你已经测试过不会影响操作性能的大小。参见Mike Walsh的《Don't Touch that Shrink Database Button In SQL Server!》。 在收缩之前重建索引会导致索引布局不良。重建后再收缩是不好的做法。收缩会导致索引被破坏以恢复空间,因此事先重建再收缩是没有意义的。参见Thomas LaRock的《When to use Auto Shrink》。这样,其中只会保留数据库的系统对象、统计信息、过程等数据。收缩操作应该会快得多,而且对于主要数据对象来说,不再需要进行任何进一步的索引维护,这些对象已经按照顺序创建,未来碎片化的风险也很小。