上週客戶反饋系統在處理大量數據時頻繁出現網絡超時。經排查發現,罪魁禍首是索引碎片率過高,導致查詢計劃退化、I/O 激增。解決方案很明確:定期重新組織或重新生成索引。為了通用、可控、可調度,我編寫了通用存儲過程 sp_RefreshIndex。
ALTER INDEX ... REORGANIZE(在線、輕量、不鎖表)。ALTER INDEX ... REBUILD(重建統計信息、壓縮頁面、可選 ONLINE=ON)。USE master;
GO
IF OBJECT_ID('sp_RefreshIndex') IS NOT NULL
DROP PROC sp_RefreshIndex;
GO
CREATE PROC sp_RefreshIndex
@Reorganize_Fragmentation_Percent SMALLINT = 5, -- 重組閾值
@Rebuild_Fragmentation_Percent SMALLINT = 30 -- 重建閾值
AS
BEGIN
/* 調用示例:
- 當前實例所有用戶庫:EXEC sys.sp_MSforeachdb 'USE ?; EXEC sp_RefreshIndex'
- 單庫:USE YourDB; EXEC sp_RefreshIndex
*/
-- 1. 跳過系統庫
IF DB_NAME() IN ('master','model','msdb','tempdb') RETURN;
-- 2. 若無索引碎片超過重組閾值,直接返回
IF NOT EXISTS (
SELECT 1 FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL)
WHERE index_id > 0 AND avg_fragmentation_in_percent > @Reorganize_Fragmentation_Percent
) RETURN;
-- 3. 打印標題
PRINT REPLICATE('-',60) + CHAR(13)+CHAR(10) +
REPLICATE(' ',14) + N'對數據庫 ' + QUOTENAME(DB_NAME()) + N' 進行索引優化' +
REPLICATE(' ',20) + CHAR(13)+CHAR(10);
DECLARE @sql NVARCHAR(2000), @msg NVARCHAR(2000);
-- 4. 游標遍歷需維護的索引
DECLARE cur_x CURSOR LOCAL FAST_FORWARD FOR
SELECT
'ALTER INDEX ' + QUOTENAME(i.name) + ' ON ' +
QUOTENAME(SCHEMA_NAME(i.object_id)) + '.' + QUOTENAME(OBJECT_NAME(i.object_id)) +
CASE WHEN s.avg_fragmentation_in_percent <= @Rebuild_Fragmentation_Percent
THEN ' REORGANIZE;' ELSE ' REBUILD;' END AS [sql],
CASE WHEN s.avg_fragmentation_in_percent <= @Rebuild_Fragmentation_Percent
THEN N'重新組織索引:' ELSE N'重新生成索引:' END +
QUOTENAME(SCHEMA_NAME(i.object_id)) + '.' +
QUOTENAME(OBJECT_NAME(i.object_id)) + '.' + QUOTENAME(i.name) AS [msg]
FROM sys.indexes i
INNER JOIN sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) s
ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE i.index_id > 0
AND s.avg_fragmentation_in_percent > @Reorganize_Fragmentation_Percent
ORDER BY OBJECT_NAME(i.object_id), i.index_id;
OPEN cur_x;
FETCH NEXT FROM cur_x INTO @sql, @msg;
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC(@sql); -- 執行維護命令
PRINT @msg; -- 記錄日誌
FETCH NEXT FROM cur_x INTO @sql, @msg;
END
CLOSE cur_x;
DEALLOCATE cur_x;
END;
GO
-- 標記為系統對象,便於 sp_MSforeachdb 直接調用
EXEC sp_ms_marksystemobject 'sp_RefreshIndex';
GO
USE YourBusinessDB;
EXEC sp_RefreshIndex; -- 使用默認閾值 5% / 30%
-- 或自定義閾值
EXEC sp_RefreshIndex @Reorganize_Fragmentation_Percent = 10, @Rebuild_Fragmentation_Percent = 40;
EXEC sys.sp_MSforeachdb 'USE ?; EXEC sp_RefreshIndex';
EXEC sys.sp_MSforeachdb 'USE ?; EXEC sp_RefreshIndex';REBUILD 預設為離線操作(鎖表),企業版可加 WITH (ONLINE = ON) 實現線上重建;標準版請安排維護窗口。BULK_LOGGED 恢復模式。WAITFOR DELAY '00:00:01' 緩解 I/O 壓力;或記錄執行耗時到日誌表供事後分析。sp_RefreshIndex 以「雙閾值 + 游標 + 系統對象」三板斧,解決了索引碎片維護的通用性、自動化、可視化痛點。部署一次,長期受益,是 DBA 工具箱裡的必備利器。