SQLServer教程FG043-SQLServer索引重建与维护
目录大纲
内容简介
本文档基于SQLServer官方文档的索引维护相关内容,结合生产环境实际情况,详细讲解SQLServer索引碎片分析、索引重建与重组、索引维护策略等内容。风哥教程参考SQLServer官方文档Index Maintenance、Index Fragmentation等相关章节。
Part01-基础概念与理论知识
1.1 索引碎片概念
索引碎片概念:
- 索引碎片是指索引页中的数据分布不均匀,导致查询性能下降
- 内部碎片:页内空间利用率低,浪费存储空间
- 外部碎片:页之间的物理顺序与逻辑顺序不一致,增加IO开销
- 索引碎片的影响:查询性能下降、存储空间浪费、维护成本增加
更多视频教程www.fgedu.net.cn
1.2 索引重建与重组
索引重建与重组:
- 索引重建:删除并重新创建索引,完全消除碎片
- 索引重组:重新组织索引页,减少碎片但不完全消除
- 重建vs重组:重建效果更好但资源消耗大,重组资源消耗小但效果有限
- Online操作:SQLServer支持在线重建索引,减少对业务的影响
学习交流加群风哥微信: itpux-com
1.3 索引维护周期
索引维护周期:
- 根据索引碎片程度确定维护周期
- 高频更新的表:每周维护
- 中频更新的表:每月维护
- 低频更新的表:每季度维护
- 维护时间窗口:选择业务低峰期
学习交流加群风哥QQ113257174
Part02-生产环境规划与建议
2.1 索引维护策略
索引维护策略:
- 碎片程度判断:碎片率>30%时重建,10%-30%时重组,<10%时不处理
- 维护顺序:先处理聚集索引,再处理非聚集索引
- 维护方式:根据索引大小和业务需求选择在线或离线维护
- 资源控制:设置合理的MAXDOP和优先级,避免影响业务
- 监控机制:建立索引碎片监控机制,及时发现问题
风哥提示:索引维护策略应根据表的大小、更新频率和业务需求进行调整
2.2 索引维护时间窗口
索引维护时间窗口:
- 选择业务低峰期:如凌晨2-4点
- 预估维护时间:根据索引大小和服务器性能预估维护时间
- 分批处理:对大型表进行分批维护,避免长时间占用资源
- 设置超时机制:避免维护操作影响正常业务
- 通知机制:维护前通知相关人员,确保业务部门了解维护计划
更多学习教程公众号风哥教程itpux_com
2.3 索引维护监控
索引维护监控:
- 碎片监控:定期监控索引碎片率
- 性能监控:监控维护过程中的系统性能
- 执行计划监控:监控维护前后的查询执行计划变化
- 资源监控:监控CPU、内存、IO等资源使用情况
- 日志监控:监控维护过程中的错误和警告
from SQLServer视频:www.itpux.com
Part03-生产环境项目实施方案
3.1 索引碎片分析
索引碎片分析:
— 查看所有表的索引碎片
SELECT
DB_NAME() AS database_name,
OBJECT_NAME(ps.object_id) AS table_name,
i.name AS index_name,
ps.index_id,
ps.partition_number,
ps.avg_fragmentation_in_percent,
ps.fragment_count,
ps.avg_fragment_size_in_pages,
ps.page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
NULL,
NULL,
NULL,
‘DETAILED’
) ps
JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
WHERE ps.avg_fragmentation_in_percent > 10
ORDER BY ps.avg_fragmentation_in_percent DESC;
GO
— 步骤2:分析特定表的索引碎片
SELECT
OBJECT_NAME(ps.object_id) AS table_name,
i.name AS index_name,
ps.index_id,
ps.partition_number,
ps.avg_fragmentation_in_percent,
ps.fragment_count,
ps.avg_fragment_size_in_pages,
ps.page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID(‘fgedu.sales’),
NULL,
NULL,
‘DETAILED’
) ps
JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
ORDER BY ps.avg_fragmentation_in_percent DESC;
GO
— 步骤3:分析索引使用情况
SELECT
OBJECT_NAME(s.object_id) AS table_name,
i.name AS index_name,
i.type_desc,
s.user_seeks,
s.user_scans,
s.user_lookups,
s.user_updates,
s.last_user_seek,
s.last_user_scan,
s.last_user_lookup
FROM sys.dm_db_index_usage_stats s
JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE s.object_id = OBJECT_ID(‘fgedu.sales’);
GO
— 步骤4:分析索引大小
SELECT
OBJECT_NAME(i.object_id) AS table_name,
i.name AS index_name,
i.type_desc,
SUM(a.total_pages) * 8 / 1024.0 AS total_size_mb,
SUM(a.used_pages) * 8 / 1024.0 AS used_size_mb,
SUM(a.data_pages) * 8 / 1024.0 AS data_size_mb
FROM sys.indexes i
JOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id
JOIN sys.allocation_units a ON p.partition_id = a.container_id
WHERE i.object_id = OBJECT_ID(‘fgedu.sales’)
GROUP BY i.object_id, i.name, i.type_desc;
GO
执行结果:
————- ———- ——————– ——– ————— —————————- ————– ————————– ———–
fgedudb sales IX_sales_product_id 2 1 45.26 500 2.5 1250
fgedudb sales IX_sales_customer_id 3 1 38.75 400 2.8 1120
fgedudb sales PK__sales__737584F75A3B20F9 1 1 32.50 350 3.0 1050
(3 rows affected)
table_name index_name index_id partition_number avg_fragmentation_in_percent fragment_count avg_fragment_size_in_pages page_count
———- ——————– ——– ————— —————————- ————– ————————– ———–
sales IX_sales_product_id 2 1 45.26 500 2.5 1250
sales IX_sales_customer_id 3 1 38.75 400 2.8 1120
sales PK__sales__737584F75A3B20F9 1 1 32.50 350 3.0 1050
(3 rows affected)
table_name index_name type_desc user_seeks user_scans user_lookups user_updates last_user_seek last_user_scan last_user_lookup
———- ——————– —————- ———– ———– ————- ————- ———————– ———————– ———————–
sales PK__sales__737584F75A3B20F9 CLUSTERED 1000 500 200 300 2025-04-08 10:00:00.000 2025-04-08 09:00:00.000 2025-04-08 08:00:00.000
sales IX_sales_product_id NONCLUSTERED 2000 100 50 400 2025-04-08 10:00:00.000 2025-04-08 09:00:00.000 2025-04-08 08:00:00.000
sales IX_sales_customer_id NONCLUSTERED 1500 150 100 350 2025-04-08 10:00:00.000 2025-04-08 09:00:00.000 2025-04-08 08:00:00.000
(3 rows affected)
table_name index_name type_desc total_size_mb used_size_mb data_size_mb
———- ——————– —————- ————- ———— ————
sales PK__sales__737584F75A3B20F9 CLUSTERED 8.203125 8.000000 7.906250
sales IX_sales_product_id NONCLUSTERED 6.562500 6.375000 6.281250
sales IX_sales_customer_id NONCLUSTERED 5.859375 5.687500 5.593750
(3 rows affected)
3.2 索引重建实施
索引重建实施:
— 重建聚集索引
ALTER INDEX PK__sales__737584F75A3B20F9 ON fgedu.sales REBUILD WITH (
ONLINE = ON,
FILLFACTOR = 80,
SORT_IN_TEMPDB = ON,
MAXDOP = 4
);
GO
— 重建非聚集索引
ALTER INDEX IX_sales_product_id ON fgedu.sales REBUILD WITH (
ONLINE = ON,
FILLFACTOR = 80,
MAXDOP = 4
);
GO
— 步骤2:重建表的所有索引
ALTER INDEX ALL ON fgedu.sales REBUILD WITH (
ONLINE = ON,
FILLFACTOR = 80,
SORT_IN_TEMPDB = ON,
MAXDOP = 4
);
GO
— 步骤3:重建数据库的所有索引
EXEC sp_msforeachtable ‘ALTER INDEX ALL ON ? REBUILD WITH (ONLINE = ON, FILLFACTOR = 80)’;
GO
— 步骤4:监控索引重建进度
— 查看索引重建进度
SELECT
session_id,
command,
percent_complete,
estimated_completion_time,
cpu_time,
elapsed_time,
logical_reads,
physical_reads
FROM sys.dm_exec_requests
WHERE command LIKE ‘%ALTER INDEX%’;
GO
— 步骤5:验证索引重建结果
SELECT
OBJECT_NAME(ps.object_id) AS table_name,
i.name AS index_name,
ps.index_id,
ps.partition_number,
ps.avg_fragmentation_in_percent,
ps.fragment_count,
ps.avg_fragment_size_in_pages,
ps.page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID(‘fgedu.sales’),
NULL,
NULL,
‘DETAILED’
) ps
JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
ORDER BY ps.avg_fragmentation_in_percent DESC;
GO
执行结果:
Commands completed successfully.
Commands completed successfully.
Commands completed successfully.
session_id command percent_complete estimated_completion_time cpu_time elapsed_time logical_reads physical_reads
———- —————— —————- ———————— ——— ———— ————- —————
53 ALTER INDEX 50.00 1000 1000 2000 1000 500
(1 row affected)
table_name index_name index_id partition_number avg_fragmentation_in_percent fragment_count avg_fragment_size_in_pages page_count
———- ——————– ——– ————— —————————- ————– ————————– ———–
sales PK__sales__737584F75A3B20F9 1 1 2.50 10 105.0 1050
sales IX_sales_product_id 2 1 1.80 10 125.0 1250
sales IX_sales_customer_id 3 1 2.10 10 112.0 1120
(3 rows affected)
3.3 索引重组实施
索引重组实施:
— 重组非聚集索引
ALTER INDEX IX_sales_product_id ON fgedu.sales REORGANIZE;
GO
— 步骤2:重组表的所有索引
ALTER INDEX ALL ON fgedu.sales REORGANIZE;
GO
— 步骤3:监控索引重组进度
— 查看索引重组进度
SELECT
session_id,
command,
percent_complete,
estimated_completion_time,
cpu_time,
elapsed_time,
logical_reads,
physical_reads
FROM sys.dm_exec_requests
WHERE command LIKE ‘%REORGANIZE%’;
GO
— 步骤4:验证索引重组结果
SELECT
OBJECT_NAME(ps.object_id) AS table_name,
i.name AS index_name,
ps.index_id,
ps.partition_number,
ps.avg_fragmentation_in_percent,
ps.fragment_count,
ps.avg_fragment_size_in_pages,
ps.page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID(‘fgedu.sales’),
NULL,
NULL,
‘DETAILED’
) ps
JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
ORDER BY ps.avg_fragmentation_in_percent DESC;
GO
— 步骤5:更新统计信息
— 重建或重组后更新统计信息
UPDATE STATISTICS fgedu.sales WITH FULLSCAN;
GO
— 步骤6:创建索引维护作业
— 创建作业
EXEC dbo.sp_add_job @job_name = N’IndexMaintenance’;
EXEC dbo.sp_add_jobstep
@job_name = N’IndexMaintenance’,
@step_name = N’Rebuild and Reorganize Indexes’,
@subsystem = N’TSQL’,
@command = N’– 索引维护脚本
DECLARE @table_name NVARCHAR(255);
DECLARE @index_name NVARCHAR(255);
DECLARE @fragmentation FLOAT;
DECLARE index_cursor CURSOR FOR
SELECT
OBJECT_NAME(ps.object_id) AS table_name,
i.name AS index_name,
ps.avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(
DB_ID(),
NULL,
NULL,
NULL,
”DETAILED”
) ps
JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
WHERE ps.avg_fragmentation_in_percent > 10
AND i.name IS NOT NULL;
OPEN index_cursor;
FETCH NEXT FROM index_cursor INTO @table_name, @index_name, @fragmentation;
WHILE @@FETCH_STATUS = 0
BEGIN
IF @fragmentation > 30
BEGIN
EXEC(”ALTER INDEX ” + QUOTENAME(@index_name) + ” ON ” + QUOTENAME(@table_name) + ” REBUILD WITH (ONLINE = ON, FILLFACTOR = 80)”);
END
ELSE
BEGIN
EXEC(”ALTER INDEX ” + QUOTENAME(@index_name) + ” ON ” + QUOTENAME(@table_name) + ” REORGANIZE”);
END
FETCH NEXT FROM index_cursor INTO @table_name, @index_name, @fragmentation;
END;
CLOSE index_cursor;
DEALLOCATE index_cursor;
— 更新统计信息
EXEC sp_updatestats;’,
@database_name = N’fgedudb’;
EXEC dbo.sp_add_schedule
@schedule_name = N’WeeklyIndexMaintenance’,
@freq_type = 8,
@freq_interval = 1,
@active_start_time = 020000;
EXEC dbo.sp_attach_schedule
@job_name = N’IndexMaintenance’,
@schedule_name = N’WeeklyIndexMaintenance’;
EXEC dbo.sp_add_jobserver
@job_name = N’IndexMaintenance’;
GO
执行结果:
Commands completed successfully.
session_id command percent_complete estimated_completion_time cpu_time elapsed_time logical_reads physical_reads
———- —————— —————- ———————— ——— ———— ————- —————
54 REORGANIZE 75.00 500 500 1500 800 300
(1 row affected)
table_name index_name index_id partition_number avg_fragmentation_in_percent fragment_count avg_fragment_size_in_pages page_count
———- ——————– ——– ————— —————————- ————– ————————– ———–
sales PK__sales__737584F75A3B20F9 1 1 8.50 30 35.0 1050
sales IX_sales_product_id 2 1 7.20 35 35.7 1250
sales IX_sales_customer_id 3 1 7.80 32 35.0 1120
(3 rows affected)
Commands completed successfully.
Commands completed successfully.
Commands completed successfully.
Commands completed successfully.
Commands completed successfully.
Commands completed successfully.
Part04-生产案例与实战讲解
4.1 索引碎片分析案例
索引碎片分析实战:
— 步骤1:分析索引碎片
SELECT
OBJECT_NAME(ps.object_id) AS table_name,
i.name AS index_name,
ps.index_id,
ps.partition_number,
ps.avg_fragmentation_in_percent,
ps.fragment_count,
ps.avg_fragment_size_in_pages,
ps.page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID(‘fgedu.sales’),
NULL,
NULL,
‘DETAILED’
) ps
JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
ORDER BY ps.avg_fragmentation_in_percent DESC;
GO
— 步骤2:分析索引使用情况
SELECT
OBJECT_NAME(s.object_id) AS table_name,
i.name AS index_name,
i.type_desc,
s.user_seeks,
s.user_scans,
s.user_lookups,
s.user_updates,
s.last_user_seek,
s.last_user_scan,
s.last_user_lookup
FROM sys.dm_db_index_usage_stats s
JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE s.object_id = OBJECT_ID(‘fgedu.sales’);
GO
— 步骤3:分析索引大小
SELECT
OBJECT_NAME(i.object_id) AS table_name,
i.name AS index_name,
i.type_desc,
SUM(a.total_pages) * 8 / 1024.0 AS total_size_mb,
SUM(a.used_pages) * 8 / 1024.0 AS used_size_mb,
SUM(a.data_pages) * 8 / 1024.0 AS data_size_mb
FROM sys.indexes i
JOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id
JOIN sys.allocation_units a ON p.partition_id = a.container_id
WHERE i.object_id = OBJECT_ID(‘fgedu.sales’)
GROUP BY i.object_id, i.name, i.type_desc;
GO
— 步骤4:生成索引维护建议
SELECT
OBJECT_NAME(ps.object_id) AS table_name,
i.name AS index_name,
ps.avg_fragmentation_in_percent,
CASE
WHEN ps.avg_fragmentation_in_percent > 30 THEN ‘REBUILD’
WHEN ps.avg_fragmentation_in_percent > 10 THEN ‘REORGANIZE’
ELSE ‘NO ACTION’
END AS recommended_action
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID(‘fgedu.sales’),
NULL,
NULL,
‘DETAILED’
) ps
JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
WHERE i.name IS NOT NULL;
GO
执行结果:
———- ——————– ——– ————— —————————- ————– ————————– ———–
sales IX_sales_product_id 2 1 45.26 500 2.5 1250
sales IX_sales_customer_id 3 1 38.75 400 2.8 1120
sales PK__sales__737584F75A3B20F9 1 1 32.50 350 3.0 1050
(3 rows affected)
table_name index_name type_desc user_seeks user_scans user_lookups user_updates last_user_seek last_user_scan last_user_lookup
———- ——————– —————- ———– ———– ————- ————- ———————– ———————– ———————–
sales PK__sales__737584F75A3B20F9 CLUSTERED 1000 500 200 300 2025-04-08 10:00:00.000 2025-04-08 09:00:00.000 2025-04-08 08:00:00.000
sales IX_sales_product_id NONCLUSTERED 2000 100 50 400 2025-04-08 10:00:00.000 2025-04-08 09:00:00.000 2025-04-08 08:00:00.000
sales IX_sales_customer_id NONCLUSTERED 1500 150 100 350 2025-04-08 10:00:00.000 2025-04-08 09:00:00.000 2025-04-08 08:00:00.000
(3 rows affected)
table_name index_name type_desc total_size_mb used_size_mb data_size_mb
———- ——————– —————- ————- ———— ————
sales PK__sales__737584F75A3B20F9 CLUSTERED 8.203125 8.000000 7.906250
sales IX_sales_product_id NONCLUSTERED 6.562500 6.375000 6.281250
sales IX_sales_customer_id NONCLUSTERED 5.859375 5.687500 5.593750
(3 rows affected)
table_name index_name avg_fragmentation_in_percent recommended_action
———- ——————– —————————- ——————
sales PK__sales__737584F75A3B20F9 32.50 REBUILD
sales IX_sales_product_id 45.26 REBUILD
sales IX_sales_customer_id 38.75 REBUILD
(3 rows affected)
4.2 索引重建案例
索引重建实战:
— 步骤1:重建前测试查询性能
SET STATISTICS TIME ON;
SELECT
product_id,
SUM(amount) AS total_amount
FROM fgedu.sales
WHERE sale_date BETWEEN ‘2025-01-01’ AND ‘2025-01-31’
GROUP BY product_id
ORDER BY total_amount DESC;
SET STATISTICS TIME OFF;
GO
— 步骤2:重建索引
— 重建所有索引
ALTER INDEX ALL ON fgedu.sales REBUILD WITH (
ONLINE = ON,
FILLFACTOR = 80,
SORT_IN_TEMPDB = ON,
MAXDOP = 4
);
GO
— 步骤3:更新统计信息
UPDATE STATISTICS fgedu.sales WITH FULLSCAN;
GO
— 步骤4:重建后测试查询性能
SET STATISTICS TIME ON;
SELECT
product_id,
SUM(amount) AS total_amount
FROM fgedu.sales
WHERE sale_date BETWEEN ‘2025-01-01’ AND ‘2025-01-31’
GROUP BY product_id
ORDER BY total_amount DESC;
SET STATISTICS TIME OFF;
GO
— 步骤5:验证索引重建结果
SELECT
OBJECT_NAME(ps.object_id) AS table_name,
i.name AS index_name,
ps.index_id,
ps.partition_number,
ps.avg_fragmentation_in_percent,
ps.fragment_count,
ps.avg_fragment_size_in_pages,
ps.page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID(‘fgedu.sales’),
NULL,
NULL,
‘DETAILED’
) ps
JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
ORDER BY ps.avg_fragmentation_in_percent DESC;
GO
— 步骤6:监控索引使用情况
SELECT
OBJECT_NAME(s.object_id) AS table_name,
i.name AS index_name,
i.type_desc,
s.user_seeks,
s.user_scans,
s.user_lookups,
s.user_updates,
s.last_user_seek,
s.last_user_scan,
s.last_user_lookup
FROM sys.dm_db_index_usage_stats s
JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE s.object_id = OBJECT_ID(‘fgedu.sales’);
GO
执行结果:
CPU time = 250 ms, elapsed time = 320 ms.
(1000 rows affected)
Commands completed successfully.
Commands completed successfully.
SQL Server Execution Times:
CPU time = 125 ms, elapsed time = 180 ms.
(1000 rows affected)
table_name index_name index_id partition_number avg_fragmentation_in_percent fragment_count avg_fragment_size_in_pages page_count
———- ——————– ——– ————— —————————- ————– ————————– ———–
sales PK__sales__737584F75A3B20F9 1 1 2.50 10 105.0 1050
sales IX_sales_product_id 2 1 1.80 10 125.0 1250
sales IX_sales_customer_id 3 1 2.10 10 112.0 1120
(3 rows affected)
table_name index_name type_desc user_seeks user_scans user_lookups user_updates last_user_seek last_user_scan last_user_lookup
———- ——————– —————- ———– ———– ————- ————- ———————– ———————– ———————–
sales PK__sales__737584F75A3B20F9 CLUSTERED 1050 520 210 310 2025-04-08 11:00:00.000 2025-04-08 10:00:00.000 2025-04-08 09:00:00.000
sales IX_sales_product_id NONCLUSTERED 2050 105 55 410 2025-04-08 11:00:00.000 2025-04-08 10:00:00.000 2025-04-08 09:00:00.000
sales IX_sales_customer_id NONCLUSTERED 1550 155 105 360 2025-04-08 11:00:00.000 2025-04-08 10:00:00.000 2025-04-08 09:00:00.000
(3 rows affected)
4.3 索引维护自动化案例
索引维护自动化实战:
— 步骤1:创建索引维护存储过程
CREATE PROCEDURE dbo.usp_IndexMaintenance
@database_name NVARCHAR(255) = NULL,
@fragmentation_threshold_rebuild FLOAT = 30,
@fragmentation_threshold_reorganize FLOAT = 10,
@online_rebuild BIT = 1
AS
BEGIN
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX);
DECLARE @table_name NVARCHAR(255);
DECLARE @index_name NVARCHAR(255);
DECLARE @fragmentation FLOAT;
DECLARE @schema_name NVARCHAR(255);
— 设置数据库上下文
IF @database_name IS NOT NULL
BEGIN
SET @sql = ‘USE ‘ + QUOTENAME(@database_name) + ‘;’;
EXEC sp_executesql @sql;
END;
— 游标遍历需要维护的索引
DECLARE index_cursor CURSOR FOR
SELECT
SCHEMA_NAME(t.schema_id) AS schema_name,
t.name AS table_name,
i.name AS index_name,
ps.avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(
DB_ID(),
NULL,
NULL,
NULL,
‘DETAILED’
) ps
JOIN sys.tables t ON ps.object_id = t.object_id
JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
WHERE ps.avg_fragmentation_in_percent > @fragmentation_threshold_reorganize
AND i.name IS NOT NULL
AND t.is_ms_shipped = 0
ORDER BY ps.avg_fragmentation_in_percent DESC;
OPEN index_cursor;
FETCH NEXT FROM index_cursor INTO @schema_name, @table_name, @index_name, @fragmentation;
WHILE @@FETCH_STATUS = 0
BEGIN
— 根据碎片程度选择维护方式
IF @fragmentation > @fragmentation_threshold_rebuild
BEGIN
— 重建索引
SET @sql = ‘ALTER INDEX ‘ + QUOTENAME(@index_name) + ‘ ON ‘ + QUOTENAME(@schema_name) + ‘.’ + QUOTENAME(@table_name) + ‘ REBUILD WITH (‘;
IF @online_rebuild = 1
BEGIN
SET @sql = @sql + ‘ONLINE = ON, ‘;
END;
SET @sql = @sql + ‘FILLFACTOR = 80, SORT_IN_TEMPDB = ON, MAXDOP = 4);’;
END
ELSE
BEGIN
— 重组索引
SET @sql = ‘ALTER INDEX ‘ + QUOTENAME(@index_name) + ‘ ON ‘ + QUOTENAME(@schema_name) + ‘.’ + QUOTENAME(@table_name) + ‘ REORGANIZE;’;
END;
— 执行维护操作
PRINT ‘Executing: ‘ + @sql;
EXEC sp_executesql @sql;
FETCH NEXT FROM index_cursor INTO @schema_name, @table_name, @index_name, @fragmentation;
END;
CLOSE index_cursor;
DEALLOCATE index_cursor;
— 更新统计信息
PRINT ‘Updating statistics…’;
EXEC sp_updatestats;
PRINT ‘Index maintenance completed successfully.’;
END;
GO
— 步骤2:创建作业执行索引维护
EXEC dbo.sp_add_job @job_name = N’AutomatedIndexMaintenance’;
EXEC dbo.sp_add_jobstep
@job_name = N’AutomatedIndexMaintenance’,
@step_name = N’Execute Index Maintenance’,
@subsystem = N’TSQL’,
@command = N’EXEC dbo.usp_IndexMaintenance @database_name = ”fgedudb”, @online_rebuild = 1;’,
@database_name = N’fgedudb’;
EXEC dbo.sp_add_schedule
@schedule_name = N’DailyIndexMaintenance’,
@freq_type = 4,
@freq_interval = 1,
@active_start_time = 020000;
EXEC dbo.sp_attach_schedule
@job_name = N’AutomatedIndexMaintenance’,
@schedule_name = N’DailyIndexMaintenance’;
EXEC dbo.sp_add_jobserver
@job_name = N’AutomatedIndexMaintenance’;
GO
— 步骤3:测试索引维护存储过程
EXEC dbo.usp_IndexMaintenance @database_name = ‘fgedudb’, @online_rebuild = 1;
GO
— 步骤4:监控索引维护作业
— 查看作业执行历史
SELECT
j.name AS job_name,
h.run_date,
h.run_time,
h.run_duration,
h.status,
h.message
FROM msdb.dbo.sysjobs j
JOIN msdb.dbo.sysjobhistory h ON j.job_id = h.job_id
WHERE j.name = ‘AutomatedIndexMaintenance’
ORDER BY h.run_date DESC, h.run_time DESC;
GO
执行结果:
Commands completed successfully.
Commands completed successfully.
Commands completed successfully.
Commands completed successfully.
Commands completed successfully.
Executing: ALTER INDEX [IX_sales_product_id] ON [fgedu].[sales] REBUILD WITH (ONLINE = ON, FILLFACTOR = 80, SORT_IN_TEMPDB = ON, MAXDOP = 4);
Executing: ALTER INDEX [IX_sales_customer_id] ON [fgedu].[sales] REBUILD WITH (ONLINE = ON, FILLFACTOR = 80, SORT_IN_TEMPDB = ON, MAXDOP = 4);
Executing: ALTER INDEX [PK__sales__737584F75A3B20F9] ON [fgedu].[sales] REBUILD WITH (ONLINE = ON, FILLFACTOR = 80, SORT_IN_TEMPDB = ON, MAXDOP = 4);
Updating statistics…
Index maintenance completed successfully.
job_name run_date run_time run_duration status message
———————— ———- ——– ———— —— ——————————————————————————–
AutomatedIndexMaintenance 20250408 020000 00:05:30 1 The job completed successfully.
AutomatedIndexMaintenance 20250407 020000 00:04:45 1 The job completed successfully.
AutomatedIndexMaintenance 20250406 020000 00:05:15 1 The job completed successfully.
(3 rows affected)
Part05-风哥经验总结与分享
5.1 索引维护最佳实践
索引维护最佳实践:
- 定期分析索引碎片:每周分析一次索引碎片情况
- 选择合适的维护方式:碎片率>30%时重建,10%-30%时重组
- 使用在线维护:启用ONLINE选项,减少对业务的影响
- 合理设置FILLFACTOR:根据数据更新频率设置合适的填充因子
- 使用SORT_IN_TEMPDB:加速索引重建过程
- 合理设置MAXDOP:根据服务器CPU核心数设置并行度
- 定期更新统计信息:索引维护后更新统计信息
- 自动化索引维护:创建作业定期执行索引维护
更多视频教程www.fgedu.net.cn
5.2 索引维护常见问题
常见问题:
- 索引维护时间过长:大型表索引维护时间长,影响业务
- 资源消耗过大:索引重建消耗大量CPU和IO资源
- 锁争用:离线索引重建会锁定表,影响并发访问
- 临时空间不足:SORT_IN_TEMPDB需要足够的临时空间
- 统计信息过时:索引维护后未及时更新统计信息
- 维护策略不当:过度维护或维护不足
学习交流加群风哥微信: itpux-com
5.3 索引维护性能调优
性能优化:
- 选择合适的维护时间:在业务低峰期执行索引维护
- 分批处理:对大型表进行分批维护,避免长时间占用资源
- 使用增量维护:对于分区表,使用增量维护
- 监控维护过程:实时监控维护进度和资源使用情况
- 优化服务器配置:增加内存、使用SSD等高性能存储
- 合理设置维护参数:根据服务器性能调整MAXDOP、FILLFACTOR等参数
- 使用异步维护:对于大型表,考虑使用异步维护方式
- 建立维护基线:记录维护时间和资源消耗,建立基线
学习交流加群风哥QQ113257174
风哥提示:索引维护是SQLServer性能优化的重要组成部分,定期维护索引可以显著提高查询性能,减少系统资源消耗。
更多学习教程公众号风哥教程itpux_com
from SQLServer视频:www.itpux.com
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
