【问题标题】:Automate INDEX rebuild based on fragmentation results?根据碎片结果自动重建索引?
【发布时间】:2012-05-08 08:45:25
【问题描述】:

是否可以添加维护作业来检查索引碎片。如果大于 50% 则自动重建这些索引?

索引大小可以从 100MB 到 10GB 不等。 SQL 2005。

谢谢。

【问题讨论】:

  • 您必须重建碎片高于 30% 的索引。是的,很可能使用 sql dmv。现在不在我的电脑上,稍后会发布脚本。

标签: sql sql-server sql-server-2005


【解决方案1】:

我使用这个脚本。请注意,我建议您阅读有关我在这里使用的 dmv 的信息,它们是 SQL2005+ 中的隐藏宝石。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
CREATE TABLE #FragmentedIndexes
(
 DatabaseName SYSNAME
 , SchemaName SYSNAME
 , TableName SYSNAME
 , IndexName SYSNAME
 , [Fragmentation%] FLOAT
)

INSERT INTO #FragmentedIndexes
SELECT
 DB_NAME(DB_ID()) AS DatabaseName
 , ss.name AS SchemaName
 , OBJECT_NAME (s.object_id) AS TableName
 , i.name AS IndexName
 , s.avg_fragmentation_in_percent AS [Fragmentation%]
FROM sys.dm_db_index_physical_stats(db_id(),NULL, NULL, NULL, 'SAMPLED') s
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id]
AND s.index_id = i.index_id
INNER JOIN sys.objects o ON s.object_id = o.object_id
INNER JOIN sys.schemas ss ON ss.[schema_id] = o.[schema_id]
WHERE s.database_id = DB_ID()
AND i.index_id != 0
AND s.record_count > 0
AND o.is_ms_shipped = 0
DECLARE @RebuildIndexesSQL NVARCHAR(MAX)
SET @RebuildIndexesSQL = ''
SELECT
 @RebuildIndexesSQL = @RebuildIndexesSQL +
CASE
 WHEN [Fragmentation%] > 30
   THEN CHAR(10) + 'ALTER INDEX ' + QUOTENAME(IndexName) + ' ON '
      + QUOTENAME(SchemaName) + '.'
      + QUOTENAME(TableName) + ' REBUILD;'
 WHEN [Fragmentation%] > 10
    THEN CHAR(10) + 'ALTER INDEX ' + QUOTENAME(IndexName) + ' ON '
    + QUOTENAME(SchemaName) + '.'
    + QUOTENAME(TableName) + ' REORGANIZE;'
END
FROM #FragmentedIndexes
WHERE [Fragmentation%] > 10
DECLARE @StartOffset INT
DECLARE @Length INT
SET @StartOffset = 0
SET @Length = 4000
WHILE (@StartOffset < LEN(@RebuildIndexesSQL))
BEGIN
 PRINT SUBSTRING(@RebuildIndexesSQL, @StartOffset, @Length)
 SET @StartOffset = @StartOffset + @Length
END
PRINT SUBSTRING(@RebuildIndexesSQL, @StartOffset, @Length)
EXECUTE sp_executesql @RebuildIndexesSQL
DROP TABLE #FragmentedIndexes

另外请记住,此脚本可能会运行一段时间并阻止对您的表的访问。除非您有企业版,否则 SQL 可以在重建索引时锁定表。这将阻止使用索引对该表的所有查询,直到索引碎片整理完成。因此,不建议仅在维护窗口期间的操作时间内运行索引重建。如果您运行的是企业版,则可以使用 ONLINE=ON 选项在线对索引进行碎片整理。这将使用更多空间,但在碎片整理操作期间您的表不会被阻塞/锁定。

如果您需要更多信息,请大声喊叫。

更新:

如果您在较小的数据库上运行此查询,您可能可以在调用 sys.dm_db_index_physical_stats 时使用“DETAILED”参数。这可能是对索引的更详细检查。 cmets 中的讨论还将指出,在更大的表上,可能值得进行 SAMPLED 扫描,因为这将有助于减少进行索引扫描所需的时间。

【讨论】:

  • 如果使用 SAMPLED,则不处理非叶级索引。 form BOL:使用 SAMPLED 模式获取compressed_pa​​ge_count 的估计值,使用 DETAILED 模式获取compressed_pa​​ge_count 的实际值。 SAMPLED 模式基于索引或堆中所有页面的 1% 样本返回统计信息。 SAMPLED 模式下的结果应视为近似值
  • 意思是如果你使用SAMPLED你必须意识到你可能得不到准确的结果
  • 确实,但 500 000 000 行表的 1% 是 5 000 000。这在某些情况下已经足够了。这实际上取决于索引的大小和您必须进行碎片整理的窗口。
  • 我将它与 Azure SQL 一起使用,它在每个版本中都支持 ALTER INDEX REBUILD WITH (ONLINE=ON)。我还删除了使用 REORGANIZE 的 case 语句的第二部分。 Azure SQL 当前不支持 ALTER INDEX 的 REORGANIZE 修饰符。您可能还想添加一个 try/catch 块,以便那些不支持在线索引的表不会导致整个操作失败。
【解决方案2】:

如果您想避免创建任何临时表并解析字符串以创建 SQL 字符串列表。这是实现它的有效方法:

USE databasename

GO

DECLARE @Queryresult NVARCHAR(4000)

SET @Queryresult=''

SELECT

  @Queryresult=@Queryresult + 'ALTER INDEX ' + QUOTENAME(i.name) + ' ON '
  + QUOTENAME('dbo') + '.'
  + QUOTENAME(OBJECT_NAME(i.OBJECT_ID)) + ' REBUILD;'

 FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'SAMPLED') ss

  INNER JOIN sys.indexes i ON i.OBJECT_ID = ss.OBJECT_ID AND i.index_id = ss.index_id

  INNER JOIN sys.objects o ON ss.object_id = o.object_id

WHERE ss.avg_fragmentation_in_percent > 50

AND ss.record_count > 0 

AND o.is_ms_shipped = 0 --Excludes any objects created as a part of SQL Server installation

AND ss.index_id > 0     --Excludes heap indexes

EXEC sp_executesql @Queryresult

【讨论】:

    【解决方案3】:

    是的,你可以。

    您可以使用此查询获取碎片索引:

    SELECT OBJECT_NAME(i.OBJECT_ID) AS TableName,
    i.name AS IndexName,
    indexstats.avg_fragmentation_in_percent
    FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'DETAILED') indexstats
    INNER JOIN sys.indexes i ON i.OBJECT_ID = indexstats.OBJECT_ID
    AND i.index_id = indexstats.index_id
    WHERE indexstats.avg_fragmentation_in_percent > 20
    

    并根据结果构建一个命令来重新创建它们

    我会将所有内容包装在存储过程中并从 SQL Server 作业中调用它

    仅供参考,50% 是一个非常大的碎片。我会少一些。

    【讨论】:

    • 使用 'DETAILED' 参数调用的 sys.dm_db_index_physical_stats 函数可能会在您拥有超过 500 000 000 行数的数据后运行。在处理更大的表时,SAMPLED 参数可以帮助减少分析索引所花费的时间,并且您实际上可以按时完成索引重建。请注意,我喜欢你实际上知道 DMV 的事实,这是一种黑带功夫艺术!
    • 如果您使用 SAMPLED,您可能无法获得准确的结果,因为您将仅扫描索引的叶级别:使用 SAMPLED 模式获取compressed_pa​​ge_count 的估计值,并使用 DETAILED 模式获取compressed_pa​​ge_count 的实际值。 SAMPLED 模式基于索引或堆中所有页面的 1% 样本返回统计信息。 SAMPLED 模式下的结果应视为近似值
    • 确实如此,但如果您需要在 2 小时内对 100 000 000 行表进行碎片整理,有时您需要做出权衡。请注意,如果您要对需要保持在线和可用的大型表进行碎片整理,这可能是解决方案。请注意,使用 SAMPLED 还具有以下效果如果索引或堆的页面少于 10,000 个,则使用 DETAILED 参数。我刚刚发现,大多数情况下,这实际上可以让您在维护时段内完成工作。
    【解决方案4】:

    您可以使用sys.dm_db_index_physical_stats 获取有关索引碎片的信息(请参阅 avg_fragmentation_in_percent 列)。然后,您可以在达到阈值时使用rebuild 子句执行alter index。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多