为什么我的数据库在重建并重新索引所有内容后仍然碎片化?

Jus*_*ing 43 index sql-server-2008 fragmentation

我有一个数据库,我试图通过运行此 T-SQL 一次对所有表进行碎片整理:

SELECT 
        'ALTER INDEX all ON ' + name + ' REORGANIZE;' + CHAR(10) +
        'ALTER INDEX all ON ' + name + ' REBUILD;'
    FROM sys.tables
Run Code Online (Sandbox Code Playgroud)

然后将输出复制并粘贴到新的查询窗口并运行它。我没有错误,但我仍然有碎片。我也尝试分别运行这两个命令,但仍然存在碎片。注意:REORGANIZE Aaron已经让我知道这是不必要的,并且我知道我可以使用动态 sql 来自动执行此操作。

我运行这个以确定我仍然有碎片:

SELECT * FROM 
sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, NULL) 
WHERE avg_fragmentation_in_percent > 0
Run Code Online (Sandbox Code Playgroud)

我得到了:

database_id object_id   index_id    partition_number    index_type_desc alloc_unit_type_desc    index_depth index_level avg_fragmentation_in_percent    fragment_count  avg_fragment_size_in_pages  page_count  avg_page_space_used_in_percent  record_count    ghost_record_count  version_ghost_record_count  min_record_size_in_bytes    max_record_size_in_bytes    avg_record_size_in_bytes    forwarded_record_count  compressed_page_count
85  171147655   1   1   CLUSTERED INDEX IN_ROW_DATA 2   0   36.3636363636364    5   2.2 11  NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
85  421576540   1   1   CLUSTERED INDEX IN_ROW_DATA 2   0   75  7   1.14285714285714    8   NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
85  965578478   1   1   CLUSTERED INDEX IN_ROW_DATA 2   0   14.7058823529412    6   5.66666666666667    34  NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
85  1061578820  1   1   CLUSTERED INDEX IN_ROW_DATA 2   0   40  4   1.25    5   NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
85  1109578991  1   1   CLUSTERED INDEX IN_ROW_DATA 2   0   30.7692307692308    5   2.6 13  NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
85  1205579333  2   1   NONCLUSTERED INDEX  IN_ROW_DATA 2   0   50  5   1.6 8   NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
85  1493580359  1   1   CLUSTERED INDEX IN_ROW_DATA 2   0   50  6   1.66666666666667    10  NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL    NULL
Run Code Online (Sandbox Code Playgroud)

我知道我缺少一些真正的基本知识,但我不知道是什么。

Mar*_*ith 38

桌子很小。表格中的页数为:

11, 8, 6, 5, 13, 8, 10

它们总共占用 480kb。几乎没有什么可以碎片整理的。

编辑:这需要更多的解释。

一个新的表或索引通常被分配到混合区的前 8 页,而不是统一区。因此,前 8 个页面中的每一个都可以从不同的混合区分配。因此,消耗 8 页的表或索引可能有 8 个片段,8 个不同的混合区中的每一个上有 1 个片段。

因此,更广泛使用的碎片整理脚本(下面链接的几个示例)倾向于排除小表。IIRC,<500 页在其中一个或两个中。在这些大小下,碎片整理几乎没有好处,并且碎片数字可能会因混合区分配而产生偏差。

  • +1 同意马克。当您实际拥有一些数据时,请担心碎片化。:-) (3认同)
  • 不是不能,而是为什么要打扰?这样做对 I/O 几乎没有影响 - 特别是因为这么小的表几乎可以保证无论如何都在内存中。 (3认同)

Mar*_*ian 21

引用自“ Microsoft SQL Server 2000 索引碎片整理最佳实践”:

“碎片会影响磁盘 I/O。因此,关注较大的索引,因为它们的页面不太可能被 SQL Server 缓存。使用 DBCC SHOWCONTIG 报告的页面计数来了解索引的大小(每个页面是8 KB 大小。通常,您不应该关心少于 1,000 页的索引的碎片级别。在测试中,包含超过 10,000 页的索引实现了性能提升,其中页数明显更多(更大超过 50,000 页)。”

所以这种回答你的问题并支持马克和亚伦的答案。

您可以在 Brent Ozar 的以下文章中找到有关索引碎片的好信息:

此外,在Kimberly Tripp 的博客上可以找到关于索引(也关于碎片问题)的大量信息。


Aar*_*and 13

这不是为了回答你的问题,但它永远不会适合评论。您可以动态构建此脚本,而无需将输出复制并粘贴到另一个窗口中。考虑到绝对没有理由REORGANIZE然后REBUILD

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += N'ALTER INDEX all ON ' + name + ' REBUILD;
    ' FROM sys.tables;

PRINT @sql; -- to see the first 8,000 characters and make sure it checks out
-- EXEC sp_executesql @sql;
Run Code Online (Sandbox Code Playgroud)