Sql-Server

數據庫維護每天重建索引

  • June 15, 2018

我的數據庫大約 2.5TB。每天執行重建索引是一個好習慣嗎?

我們每天刪除大約 20 GB 的數據,同時我們刪除分區,我們將添加新分區。執行重建索引需要 8 小時,有時需要 24 小時。在此期間,日誌文件增加到 300 GB。

我們需要每天向客戶發送數據庫碎片報告。看到 99% 或 98%,他們會害怕。請建議。

表大小為 70GB,對於此表碎片級別,每天將達到 99%。不知道是什麼讓客戶要求提供碎片級別的每日報告,但他每天都需要它。

首先,您應該考慮page_count索引的。如果page_count小於 1000 (或您決定的任何值),那麼您應該忽略 index

我們有一個維護腳本,它分析page_count索引的碎片並遵循以下準則:-

  • 少於 10% 的邏輯碎片,不做任何事情
  • 10% 到 30% 之間的邏輯碎片,重新組織它(使用ALTER INDEX … REORGANIZE
  • 超過 30% 的邏輯碎片,重建它(使用ALTER INDEX … REBUILD)(ONLINE= ON對於企業版)

這是了解您目前所在位置的入門:-

SELECT dbtables.[name] AS 'Table'
   ,dbindexes.[name] AS 'Index'
   ,indexstats.avg_fragmentation_in_percent
   ,CASE 
       WHEN indexstats.avg_fragmentation_in_percent < 10
           THEN 'NOTHING'
       WHEN indexstats.avg_fragmentation_in_percent >= 10
           AND indexstats.avg_fragmentation_in_percent < 30
           THEN 'REORGANIZE'
       WHEN indexstats.avg_fragmentation_in_percent >= 30
           THEN 'REBUILD'
       END
   ,indexstats.page_count
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) AS indexstats
INNER JOIN sys.tables dbtables ON dbtables.[object_id] = indexstats.[object_id]
INNER JOIN sys.schemas dbschemas ON dbtables.[schema_id] = dbschemas.[schema_id]
INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id]
   AND indexstats.index_id = dbindexes.index_id
WHERE indexstats.database_id = DB_ID()
   AND page_count > 1000
   AND dbindexes.NAME IS NOT NULL
ORDER BY indexstats.avg_fragmentation_in_percent DESC

這一切都取決於您的要求。如果您每週只重建一次,性能會受到怎樣的影響?您可以在重建後直接每週發送一次報告嗎?我會在測試環境中設定性能基準(如果可能的話),如果它不會降低太多,那麼每週只執行一次索引維護。

代理工作可能是這樣的:-

DECLARE @Indexes AS TABLE (
   ID INT IDENTITY(1, 1) NOT NULL
   ,TableNm VARCHAR(500) NOT NULL
   ,IndexNm VARCHAR(500) NOT NULL
   ,FragPerc DECIMAL(16, 3) NOT NULL
   ,RecAction VARCHAR(50) NOT NULL
   ,PageCount INT NOT NULL
   )
INSERT INTO @Indexes
SELECT dbtables.[name] AS 'Table'
   ,dbindexes.[name] AS 'Index'
   ,indexstats.avg_fragmentation_in_percent
   ,CASE 
       WHEN indexstats.avg_fragmentation_in_percent < 10 --change all levels to your requirement
           THEN 'NOTHING'
       WHEN indexstats.avg_fragmentation_in_percent >= 10
           AND indexstats.avg_fragmentation_in_percent < 30
           THEN 'REORGANIZE'
       WHEN indexstats.avg_fragmentation_in_percent >= 30
           THEN 'REBUILD'
       END
   ,indexstats.page_count
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) AS indexstats
INNER JOIN sys.tables dbtables ON dbtables.[object_id] = indexstats.[object_id]
INNER JOIN sys.schemas dbschemas ON dbtables.[schema_id] = dbschemas.[schema_id]
INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id]
   AND indexstats.index_id = dbindexes.index_id
WHERE indexstats.database_id = DB_ID()
   AND page_count > 1000
   AND dbindexes.NAME IS NOT NULL
ORDER BY indexstats.avg_fragmentation_in_percent DESC

DELETE FROM @Indexes WHERE RecAction = 'NOTHING'

DECLARE @sql AS VARCHAR(MAX)
DECLARE @whilecount AS INT = 1
DECLARE @rowcount AS INT = (
       SELECT MAX(ID)
       FROM @Indexes
       )
DECLARE @IndexNm AS VARCHAR(500)
DECLARE @TableNm AS VARCHAR(500)
DECLARE @RecAction AS VARCHAR(50)

WHILE @whilecount <= @rowcount
BEGIN
   SET @IndexNm = (
           SELECT IndexNm
           FROM @Indexes
           WHERE ID = @whilecount
           )
   SET @TableNm = (
           SELECT TableNm
           FROM @Indexes
           WHERE ID = @whilecount
           )
   SET @RecAction = (
           SELECT RecAction
           FROM @Indexes
           WHERE ID = @whilecount
           )

   IF @RecAction = 'REBUILD'
   BEGIN
       SET @sql = 'ALTER INDEX [' + @IndexNm + '] ON [' + @TableNm + '] REBUILD WITH (FILLFACTOR = 98, ONLINE = ON);' --change depending on SQL version
   END
   ELSE IF @RecAction = 'REORGANIZE'
   BEGIN
       SET @sql = 'ALTER INDEX [' + @IndexNm + '] ON [' + @TableNm + '] REORGANIZE;'
   END

   EXECUTE (@sql);

   SET @whilecount += 1
END 

引用自:https://dba.stackexchange.com/questions/151084