在不添加/删除聚集索引的情况下减少 SQL Server 表碎片?

Posted

技术标签:

【中文标题】在不添加/删除聚集索引的情况下减少 SQL Server 表碎片?【英文标题】:Reduce SQL Server table fragmentation without adding/dropping a clustered index? 【发布时间】:2011-03-21 04:46:11 【问题描述】:

我有一个大型数据库(90GB 数据,70GB 索引),在过去的一年里一直在缓慢增长,增长/变化不仅导致了索引的大量内部碎片,还导致了表本身的大量内部碎片。

解决(大量)非常碎片化的索引很容易 - REORGANIZE 或 REBUILD 会处理这个问题,具体取决于它们的碎片化程度 - 但我能找到的关于清理实际表碎片的唯一建议是添加表的聚集索引。之后我会立即删除它,因为我不希望表上的聚集索引继续前进,但是没有聚集索引还有另一种方法吗?执行此操作的“DBCC”命令?

感谢您的帮助。

【问题讨论】:

你为什么不想要一个聚集索引? 仅添加一个“ID”标识 bigint 列作为聚集索引不仅可以解决您的碎片问题,还可能会使您的所有其他索引更小。 您是否进行了测试以确认聚集索引会严重影响性能?顺便说一句,聚集索引不必位于主键上。 @RobinDay。索引是必需的,至少需要一个来提供行唯一性(与记录 ID 唯一性相反)。因此不能删除索引,也不能实现数据库收缩。将 PK 更改为 ID,然后将其迁移到所有子表将减小大小,但这是不可行的,因为失去了关系完整性和 JOIN 能力。更不用说,根据给出的原因,OP 不能。 @user4154343。 创建聚集索引然后将其删除以减少堆中的碎片是一个坏主意 是错误的且具有误导性。 Randall 特别建议创建一个聚集索引并将其永久保留 【参考方案1】:

问题

让我们澄清一下,因为这是一个常见问题,对于每个使用 SQL Server 的公司来说都是一个严重的问题。

这个问题以及对 CREATE CLUSTERED INDEX 的需求被误解了。

同意拥有永久聚集索引总比没有要好。但这不是重点,无论如何它都会导致长时间的讨论,所以让我们把它放在一边,专注于发布的问题。

关键是,上有大量碎片。您一直称它为“表”,但在物理数据存储或 DataStructure 级别没有这样的东西。表是一个逻辑概念,而不是物理概念。它是物理数据结构的集合。该集合是两种可能性之一:

加上所有非聚集指数 加上文本/图像链

聚集索引 (消除堆和一个非聚集索引) 加上所有非聚集指数 加上文本/图像链。

堆严重碎片化;散布的(随机)插入/删除/更新越多,碎片就越多。

没有办法按原样清理堆。 MS 不提供设施(其他供应商提供)。

解决方案

但是,我们知道 Create Clustered Index 完全重写和重新排序堆。因此,该方法(不是技巧)是创建聚集索引仅用于对堆进行碎片整理,然后将其删除。您需要 table_size x 1.25 的数据库中的可用空间。

当您使用它时,请务必使用 FILLFACTOR,以减少 未来 碎片化。然后,堆将占用更多分配的空间,允许将来由于更新而进行插入、删除和行扩展。

注意

    请注意,分片分为三个级别;这仅涉及级别 III,堆内的碎片,这是由缺少聚集索引

    引起的

    作为一项单独的任务,在其他时间,您可能希望考虑实施永久聚集索引,它可以完全消除碎片......但这与已发布的问题是分开的。

回复评论

SqlRyan: 虽然这并没有为我的问题提供一个神奇的解决方案,但很明显我的问题是 SQL Server 限制的结果,添加聚集索引是“整理”堆的唯一方法。

不完全是。我不会称其为“限制”。

    我给出的消除堆中碎片的方法是创建一个聚集索引,然后删除它。即。暂时,唯一的目的就是纠正分片。

    在表上(永久)实现聚集索引是一个更好的解决方案,因为它减少了总体碎片化(DataStructure 仍然可以碎片化,请参阅下面链接中的详细信息),这远远小于堆中发生的碎片。

    关系数据库中的每个表(“管道”或“队列”表除外)都应该有一个聚集索引,以便利用它的各种优势。

    聚集索引应该在分布数据的列上(避免 INSERT 冲突),永远不要在单调递增的列上建立索引,例如记录 ID 1,这样可以保证 INSERT Hot在最后一页找到。

1.每个文件上的记录 ID 将您的“数据库”呈现为非关系记录归档系统,使用 SQL 只是为了方便。此类文件没有关系数据库的完整性、功能或速度。

安德鲁·希尔: 您能否进一步评论“请注意,存在三个碎片级别;这仅涉及第三级”——其他两个碎片级别是什么?

在 MS SQL 和 Sybase ASE 中,分片分为三个级别,在每个级别中,有几个不同的类型。请记住,在处理 Fragmentation 时,我们必须关注 DataStructures,而不是表(表是 DataStructures 的集合,如上所述)。级别是:

I 级 • 额外数据结构 在相关的 DataStructure 之外,跨数据库或在数据库内。

二级 • 数据结构 在相关的 DataStructure 中,在页面上方(跨所有页面) 这是 DBA 最常处理的级别。

III 级 • 页面 在相关的数据结构内,在页面内

这些链接提供有关碎片的完整详细信息。它们特定于 Sybase ASE,但是,在结构级别,这些信息适用于 MS SQL。

Fragmentation Definition

Fragmentation Impact

Fragmentation Type

注意我给出的方法是Level II,它修正了Level II和III Fragmentation。

【讨论】:

你能看看我的问题***.com/questions/3800732/… 吗? 虽然这并没有为我的问题提供神奇的解决方案,但很明显我的问题是 SQL Server 限制的结果,添加聚集索引是“碎片整理”的唯一方法堆。感谢您的帮助。 您能否进一步评论“请注意,存在三个碎片级别;这仅涉及第三级”——其他两个碎片级别是什么?【参考方案2】:

没有人谈论的问题是硬盘驱动器本身上的数据或日志设备文件的碎片!每个人都在谈论索引的碎片以及如何避免/限制碎片。

仅供参考:当您创建数据库时,您指定 .MDF 的初始大小以及它在需要增长时会增长多少。您对 .LDF 文件执行相同操作。无法保证当这两个文件增长时,为所需的额外磁盘空间分配的磁盘空间将与分配的现有磁盘空间物理上连续!!

每次这两个设备文件中的一个需要扩展时,就有可能造成硬盘磁盘空间的碎片。这意味着硬盘驱动器上的磁头需要更加努力地工作(并且需要更多时间)才能从硬盘驱动器的一个部分移动到另一个部分以访问数据库中的必要数据。这类似于购买一小块土地并在这块土地上建造一所房子。当您需要扩建房屋时,除非您购买隔壁的空地,否则您将没有更多可用的土地-除非-如果其他人同时已经购买了该土地并在其上盖了房子怎么办?然后你不能扩大你的房子。唯一的可能是在“街区”购买另一块土地,并在其上建造另一座房屋。问题变成了 - 你和你的两个孩子会住在 A 屋,而你的妻子和第三个孩子会住在 B 屋。那会很痛苦(只要你还结婚)。

解决这种情况的解决方案是“购买更大的地块,拿起现有的房屋(即数据库),将其移动到更大的地块,然后在那里扩建房屋”。好吧-您如何使用数据库来做到这一点?进行完整备份,删除数据库(除非您有足够的可用磁盘空间来保留旧的碎片数据库 - 以防万一 - 以及新数据库),创建一个分配了大量初始磁盘空间的全新数据库(不保证操作系统会确保您请求的空间是连续的),然后将数据库恢复到刚刚创建的新数据库空间。是的 - 这样做很痛苦,但我不知道有任何“自动磁盘碎片整理程序”软件可以处理 SQL 数据库文件。

【讨论】:

【参考方案3】:

我理解您受传统设计限制的痛苦。

您是否有机会在另一台服务器上恢复相关表的备份并创建聚集索引?如果在一组狭窄的唯一列或标识列上创建聚集索引,则很有可能会减少总表(数据和索引)的大小。

在我的一个旧版应用程序中,所有数据都是通过视图访问的。我能够在不影响应用程序的情况下修改基础表的架构,添加标识列和聚集索引。

堆的另一个缺点是与任何转发的行相关的额外 IO。

当我被问及是否有任何证据表明我们需要在表上永久使用聚集索引时,我发现下面的文章很有效

This article is by Microsoft

【讨论】:

【参考方案4】:

您可以通过运行DBCC SHRINKFILE with NOTRUNCATE. 来压缩

基于 cmets,我发现您尚未使用永久聚集索引进行测试。

为了说明这一点,我们的数据库每天有 1000 万新行,所有表都有聚集索引。删除的“间隙”将通过预定的 ALTER INDEX (以及转发指针/页面拆分)删除。

您的 12GB 表在索引后可能是 2GB:它仅分配了 12GB,但也有大量碎片。

【讨论】:

【参考方案5】:

您声明您添加了一个聚集索引来缓解表碎片,然后立即将其删除。

聚簇索引通过对聚簇键进行排序来消除碎片,但您说该键将来无法使用。这就引出了一个问题:为什么要使用此密钥进行碎片整理?

创建这个集群键并保留它是有意义的,因为您显然希望/需要以这种方式排序的数据。你说数据变化会招致无法承受的数据移动惩罚;您是否考虑过使用低于默认值的FILLFACTOR 创建索引?根据数据更改模式,您可以从低至 80% 的数据中受益。然后,您每页有 20% 的“未使用”空间,但在更改聚集键值时减少页面拆分的好处。

对你有帮助吗?

【讨论】:

以上是关于在不添加/删除聚集索引的情况下减少 SQL Server 表碎片?的主要内容,如果未能解决你的问题,请参考以下文章

覆盖索引

在不知道对象索引的情况下从数组中删除对象?

sqlserver 在数据查询时是按时间顺序排列的 在时间字段上还有必要加聚集索引吗 为啥

sqlserver中这样的字段用啥索引比较好?

sql 删除所有非聚集索引 - SQL Server

如何在不更改现有数据位置的情况下添加新的 SQL Server 分区范围来容纳未来的数据?