SQL Server 聚集索引高碎片

Nic*_*las 6 index sql-server clustered-index fragmentation sql-server-2012

我有一个以整数(4 字节)为主键的表。它被定义为一个身份。它也是聚集索引。

插入工作非常好。插入 2000 行后,碎片大约为 4%。

但是,每条记录之后将至少更新 3 次。这会生成超过 99% 的聚集索引碎片(使用默认填充因子)。

或者使用我测试的其他填充因子: - 将聚集索引的填充因子设置为 80(碎片 > 98%) - 将聚集索引的填充系数设置为 50(碎片 > 94%)

所以这看起来没什么帮助......

我在一个新创建的表上测试了每个填充因子设置,其中包含 2000 个插入(以及 3 个以上的更新)

没有查询中不包含主键的选择或更新。

有没有人知道为什么这个索引有这么高的碎片?

通过 ADO.NET 进行更新,这是生成的命令(由 SQL Profiler 跟踪):

exec sp_executesql N'UPDATE MyTable SET MyValue = @MyValue WHERE MyId = @MyId',
N'@MyValue varchar(50),@MyId int',@MyValue='This is some random value',@MyId=1234
Run Code Online (Sandbox Code Playgroud)

其他更新遵循相同的模式(更新其他值,但始终提供@MyId 参数)

感谢您的帮助 :)

Jon*_*gel 15

FILLFACTOR仅适用于构建或重建索引时,而不适用于正常操作期间。正常操作总是尝试将页面填充到 100%。

如果您插入具有可变宽度的行,然后将该行更新为更长,如果没有足够的额外空间在同一页面上存储残像,则该行将不再适合页面。如果没有足够的空间,这将导致页面拆分,这是创建必要空间的过程。

关于页面拆分有点误导的是,有“好的拆分”和“坏的拆分”,即使性能计数器对它们进行计数。

  • 一个好的拆分是将新行添加到索引的末尾,就像您执行初始批次INSERTs时发生的情况一样。新行不适合最后一页,因此存储引擎必须分配一个新页并在逻辑上将其连接到最后一个索引页。新页面可能会在旧的最后一个索引页面之后物理存在。

  • 一个不好的分割是当一个页面必须插入索引的中间时:新页面在两侧逻辑上附加到索引结构,但可能(可能)不存在于这些页面的连续物理顺序中。

碎片是逻辑和物理顺序之间的差异,并且在大多数情况下,只有不良类型的页面拆分会导致碎片。

因为您夸大了现有行的大小,这会导致页面拆分的错误类型,这就是您看到高碎片数的原因。

目前尚不清楚您的确切过程是什么,以及最终会在此表中结束多少行。如果它类似于一次性填充,请执行完整填充过程,然后转身并使用 100% 重建聚集索引FILLFACTOR

如果此过程连续发生,您可以通过执行以下操作来“预分配”空间:向表中添加一个虚拟可变宽度列,将其填充到最大长度INSERT,然后在第一个上UPDATE,将虚拟值设置为NULL同时更新真实值。由于所有数据值都在飞来飞去,因此此方法可能会增加日志记录机制的开销。

但是,一般而言,当所有这些因素都成立时,您只需要担心碎片化:

  • 索引足够大,随机读取页面(范围)太慢(选择你自己定义的“太慢”是什么意思)
  • 索引将被扫描
  • 索引页(范围)将从磁盘物理读取(即,它们尚未在缓冲池中)
  • 存储子系统是在处理随机读取差(即主轴为基础,字节不会来缓存出来)

但是,我建议您采用可靠的索引维护解决方案,以或多或少地控制事情。您可以在此网站上搜索相关建议。

  • Jonathan Kehayias 有 [一篇关于使用扩展事件仅跟踪 *坏* 页面拆分的好帖子](http://www.sqlskills.com/blogs/jonathan/tracking-problematic-pages-splits-in-sql-server- 2012-extended-events-no-really-this-time/)。 (2认同)