博客 / 詳情

返回

數據庫索引重組與重建

不久前,遇到了一個問題。一個普通的數據庫查詢出現了超時,表的數據量不多,不應該會出現超時的情況。

時間發生在凌晨,組長説應該DBA在執行索引重組與重建腳本。哦?這是什麼我完全不知道,上百度!

什麼是索引

索引大家應該都瞭解,索引的作用就像書的目錄,可以讓數據庫在查詢時更快定位數據,而不是整表掃描。

  1. 提升查詢效率
  2. 加速排序和分組
  3. 保證唯一性

什麼是重組與重建索引呢?為什麼要這麼做?

索引不是一勞永逸的,它們會因為數據更新而“碎片化”:

  1. 插入:可能在頁中間插入新值,導致數據頁分裂。
  2. 刪除:留下空洞。
  3. 更新:可能把數據移到別的頁。

這些操作時間長了會讓索引的存儲結構(B+樹)效率下降。

所以DBA通常會定期重建索引或重組索引,來減少碎片、提高查詢性能。

為什麼多在凌晨執行

白天系統訪問量大,重建索引會鎖表/佔用大量I/O和CPU,影響業務。

凌晨訪問量小,適合做維護任務(索引重建、統計信息更新、備份)。

SQL腳本

知道了什麼是重組與重建索引,就要理解如何實現該操作呢?

找Chatgpt寫一個腳本,解析一下看看。

--關閉行計數返回
SET NOCOUNT ON;

DECLARE @SchemaName NVARCHAR(256);  --變量模式
DECLARE @TableName NVARCHAR(256);   --變量表名
DECLARE @IndexName NVARCHAR(256);   --變量索引名
DECLARE @AvgFrag DECIMAL(18,2);     --變量平均碎片率
DECLARE @sql NVARCHAR(MAX);         --變量要執行的SQL字符串

--判斷是否已存在,先刪後建
IF OBJECT_ID('tempdb..#FragList') IS NOT NULL
    DROP TABLE #FragList;

--創建臨時表
CREATE TABLE #FragList
(
    ObjectId INT,
    IndexId INT,
    SchemaName NVARCHAR(256),
    TableName NVARCHAR(256),
    IndexName NVARCHAR(256),
    AvgFragmentation DECIMAL(18,2),
    PageCount BIGINT
);

--統計索引碎片率信息
--sys.dm_db_index_physical_stats 獲取當前數據庫的索引物理統計
INSERT INTO #FragList
SELECT
    ips.object_id,
    ips.index_id,
    s.name AS SchemaName,
    t.name AS TableName,
    i.name AS IndexName,
    ips.avg_fragmentation_in_percent,
    ips.page_count
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
JOIN sys.tables t ON t.object_id = ips.object_id
JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE ips.database_id = DB_ID()
  AND i.type_desc <> 'HEAP'
  AND ips.page_count > 100; -- 可選:只處理較大的索引

--遊標批處理(等同於foreach),遍歷#FragList
DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
SELECT SchemaName, TableName, IndexName, AvgFragmentation
FROM #FragList;

OPEN cur;
FETCH NEXT FROM cur INTO @SchemaName, @TableName, @IndexName, @AvgFrag;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @sql = NULL;

    IF @AvgFrag BETWEEN 5 AND 30
        SET @sql = N'ALTER INDEX [' + @IndexName + '] ON [' + @SchemaName + '].[' + @TableName + '] REORGANIZE;';
    ELSE IF @AvgFrag > 30
        SET @sql = N'ALTER INDEX [' + @IndexName + '] ON [' + @SchemaName + '].[' + @TableName + '] REBUILD WITH (ONLINE = ON);';

    IF @sql IS NOT NULL
    BEGIN
        PRINT @sql;
        EXEC sp_executesql @sql;
    END

    FETCH NEXT FROM cur INTO @SchemaName, @TableName, @IndexName, @AvgFrag;
END

CLOSE cur;
DEALLOCATE cur;
```"
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.