我有一个大型数据库(90GB 数据,70GB 索引),在过去的一年里一直在缓慢增长,增长/变化不仅导致了索引的大量内部碎片,还导致了表本身的大量内部碎片。
解决(大量)非常碎片化的索引很容易——REORGANIZE 或 REBUILD 会处理这个问题,这取决于它们的碎片程度——但我能找到的关于清理实际表碎片的唯一建议是添加一个聚集索引到桌子上。之后我会立即删除它,因为我不希望表上的聚集索引继续前进,但是在没有聚集索引的情况下还有另一种方法吗?执行此操作的“DBCC”命令?
谢谢你的帮助。
我有一个大型数据库(90GB 数据,70GB 索引),在过去的一年里一直在缓慢增长,增长/变化不仅导致了索引的大量内部碎片,还导致了表本身的大量内部碎片。
解决(大量)非常碎片化的索引很容易——REORGANIZE 或 REBUILD 会处理这个问题,这取决于它们的碎片程度——但我能找到的关于清理实际表碎片的唯一建议是添加一个聚集索引到桌子上。之后我会立即删除它,因为我不希望表上的聚集索引继续前进,但是在没有聚集索引的情况下还有另一种方法吗?执行此操作的“DBCC”命令?
谢谢你的帮助。
让我们弄清楚,因为这是一个常见问题,对于每个使用 SQL Server 的公司来说都是一个严重的问题。
这个问题以及对 CREATE CLUSTERED INDEX 的需求被误解了。
同意拥有永久聚集索引总比没有要好。但这不是重点,无论如何它都会导致长时间的讨论,所以让我们把它放在一边,专注于发布的问题。
关键是,您在Heap上有大量碎片。您一直称它为“表”,但在物理数据存储或 DataStructure 级别没有这样的东西。表是一个逻辑概念,而不是物理概念。它是物理数据结构的集合。该系列是以下两种可能性之一:
堆
加上所有非聚集索引
加上文本/图像链
或聚集索引
(消除堆和一个非聚集索引)
加上所有非聚集索引
加上文本/图像链。
堆严重碎片化;穿插的(随机)插入/删除/更新越多,碎片就越多。
没有办法按原样清理堆。MS 不提供设施(其他供应商提供)。
但是,我们知道 Create Clustered Index 完全重写和重新排序堆。因此,该方法(不是技巧)是仅为了对 Heap 进行碎片整理而创建聚簇索引,然后将其删除。您需要 table_size x 1.25 的数据库中的可用空间。
当您使用它时,请务必使用 FILLFACTOR,以减少未来的碎片化。然后,堆将占用更多分配的空间,允许将来由于更新而进行插入、删除和行扩展。
请注意,分片分为三个级别;这仅涉及级别 III,堆内的碎片,这是由缺少聚集索引引起的
作为一项单独的任务,在其他时间,您可能希望考虑实现一个永久聚集索引,它完全消除了碎片......但这与发布的问题是分开的。
SqlRyan:
虽然这并没有为我的问题提供一个神奇的解决方案,但很清楚我的问题是由于 SQL Server 限制造成的,并且添加聚集索引是“整理”堆的唯一方法。
不完全的。我不会称其为“限制”。
我给出的消除堆中碎片的方法是创建一个聚集索引,然后删除它。IE。暂时,唯一的目的就是纠正分片。
在表上(永久)实现聚集索引是一个更好的解决方案,因为它减少了整体碎片化(DataStructure 仍然可以碎片化,请参阅下面链接中的详细信息),这远低于发生在堆中的碎片化。
关系数据库中的每个表(“管道”或“队列”表除外)都应该有一个聚集索引,以便利用它的各种好处。
聚集索引应该在分布数据的列上(避免 INSERT 冲突),永远不要在单调递增的列上建立索引,例如 Record ID 1,这保证了最后一页中的 INSERT 热点。
1. 每个文件上的记录 ID 将您的“数据库”呈现为非关系记录归档系统,使用 SQL 只是为了方便。此类文件没有关系数据库的完整性、功能或速度。
Andrew Hill:
您能否进一步评论“请注意,有三个级别的碎片化;这仅涉及 III 级”——其他两个碎片级别是什么?
在 MS SQL 和 Sybase ASE 中,存在三个碎片级别,并且在每个级别中,有几种不同的类型。请记住,在处理 Fragmentation 时,我们必须关注 DataStructures,而不是表(表是 DataStructures 的集合,如上所述)。级别是:
Level I • Extra-DataStructure
在相关DataStructure 之外,跨数据库或在数据库内。
Level II • DataStructure
在相关的DataStructure 中,在页面之上(跨越所有页面)
这是DBA 最常处理的级别。
级别 III •
相关数据结构内的页面,页面内
这些链接提供了有关碎片的完整详细信息。它们特定于 Sybase ASE,但是,在结构级别,这些信息适用于 MS SQL。
请注意,我给出的方法是 Level II,它纠正了 Level II 和 III Fragmentation。
您声明您添加了一个聚集索引以减轻表碎片,然后立即将其删除。
聚簇索引通过对聚簇键进行排序来消除碎片,但是您说该键将来无法使用。这就引出了一个问题:为什么要使用此密钥进行碎片整理?
创建这个集群键并保留它是有意义的,因为您显然希望/需要以这种方式排序的数据。你说数据变化会招致无法承受的数据移动惩罚;您是否考虑过创建低于FILLFACTOR
默认值的索引?根据数据更改模式,您可以从低至 80% 的数据中受益。然后,您每页有 20% 的“未使用”空间,但是当集群键值更改时,较低的页面拆分会带来好处。
这对你有帮助吗?
您可以通过运行带有 NOTRUNCATE 的 DBCC SHRINKFILE 来压缩堆。
根据评论,我看到您没有使用永久聚集索引进行测试。
从这个角度来看,我们的数据库每天有 1000 万个新行,所有表都有聚集索引。删除的“间隙”将通过预定的 ALTER INDEX (以及转发指针/页面拆分)删除。
您的 12GB 表在建立索引后可能是 2GB:它仅分配了 12GB,但也有大量碎片。
我理解您因受传统设计的限制而痛苦。
您是否有机会在另一台服务器上恢复相关表的备份并创建聚集索引?如果在一组狭窄的唯一列或标识列上创建聚集索引,则很有可能会减少总表(数据和索引)的大小。
在我的一个遗留应用程序中,所有数据都是通过视图访问的。我能够在不影响应用程序的情况下修改基础表的架构,添加标识列和聚集索引。
拥有堆的另一个缺点是与任何转发的行相关的额外 IO。
当我被问到是否有任何证据表明我们需要在表上永久使用聚集索引时,我发现下面的文章很有效
没有人谈论的问题是硬盘驱动器本身上的数据或日志设备文件的碎片!每个人都在谈论索引的碎片以及如何避免/限制碎片。
仅供参考:当您创建数据库时,您指定 .MDF 的初始大小以及当它需要增长时它将增长多少。您对 .LDF 文件执行相同操作。无法保证当这两个文件增长时,为所需的额外磁盘空间分配的磁盘空间将与分配的现有磁盘空间物理上连续!!
每当这两个设备文件中的一个需要扩展时,就有可能导致硬盘磁盘空间碎片化。这意味着硬盘驱动器上的磁头需要更加努力地工作(并且需要更多时间)才能从硬盘驱动器的一个部分移动到另一个部分以访问数据库中的必要数据。这类似于购买一小块土地并在这块土地上建造一所房子。当您需要扩建房屋时,除非您购买隔壁的空地,否则您将没有更多可用的土地-除非-如果其他人同时已经购买了该土地并在其上盖了房子怎么办?然后你不能扩大你的房子。唯一的可能是在“社区”购买另一块土地,并在其上建造另一座房屋。
补救这种情况的解决方案是“购买更大的地块,拿起现有的房屋(即数据库),将其移动到更大的地块,然后在那里扩建房屋”。好吧-您如何使用数据库来做到这一点?进行完整备份,删除数据库(除非您有足够的可用磁盘空间来保留旧的碎片数据库 - 以防万一 - 以及新数据库),创建一个分配了大量初始磁盘空间的全新数据库(不保证操作系统会确保您请求的空间是连续的),然后将数据库恢复到刚刚创建的新数据库空间。是的 - 这样做很痛苦,但我不知道有任何“自动磁盘碎片整理程序”软件可以处理 SQL 数据库文件。