|
|
一、先查:索引碎片率(第一步,先看哪些索引需要维护)
打开 SSMS,选中正航数据库,执行下面脚本,只看行数 > 1000 行的表(小表碎片不用管)
SELECT
OBJECT_NAME(ips.object_id) AS 表名,
i.name AS 索引名,
ips.avg_fragmentation_in_percent AS 碎片率,
ips.page_count AS 索引页数
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.index_id > 0 --排除堆
AND ips.page_count > 1000 --只看有价值的大索引
ORDER BY avg_fragmentation_in_percent DESC;
阈值规则(微软标准,ERP 推荐)
- 碎片 <10%:不用处理
- 碎片 10% ~ 30%:REORGANIZE【重组】,轻量、在线、不锁表
- 碎片>30%:REBUILD【重建】,彻底清理碎片,开销大
二、重组 REORGANIZE(碎片 10~30%,推荐优先用这个,对业务影响小)
在线操作,几乎不锁表,适合 ERP 夜间维护
-- 单表全部索引重组(例如采购子表)
ALTER INDEX ALL ON dbo.采购单据子表 REORGANIZE;
-- 只重组某一个索引(前面我们建的采购联合索引)
ALTER INDEX idx_pur_trade_date_supp ON dbo.采购单据子表 REORGANIZE;
三、重建 REBUILD(碎片 > 30%,效果最强)
⚠️ 默认离线重建,会锁表!SQL Server 企业版支持 ONLINE=ON在线重建(不锁表);标准版没有 Online 选项,只能深夜没人的时候跑
--【企业版】在线重建(业务几乎不受影响)
ALTER INDEX ALL ON dbo.采购单据子表 REBUILD WITH(ONLINE=ON);
--【标准版/Express】离线重建,必须无人时段!
ALTER INDEX ALL ON dbo.采购单据子表 REBUILD;
--重建单个索引
ALTER INDEX idx_pur_trade_date_supp ON dbo.采购单据子表 REBUILD;
四、更新统计信息(非常关键!很多报表慢不是碎片,是统计过期)
SQL 查询优化器靠统计信息生成执行计划;单据持续新增后,统计信息过期,会选错执行计划,直接全表扫描,报表卡死无响应
-- 更新单表全量统计(采购子表,推荐!)
UPDATE STATISTICS dbo.采购单据子表 WITH FULLSCAN;
-- 整库全部表统计更新(低峰执行)
EXEC sp_updatestats;
`WITH FULLSCAN`:扫描全表生成统计,而不是抽样,**大 ERP 表推荐用这个,更精准**,代价是耗时更长。
五、一键自动脚本(推荐给客户定时作业,自动判断碎片执行维护)
这个脚本自动判断碎片,大于 30% 重建,10~30% 重组,最后更新统计,直接放到 SQL Server 代理定时作业,每晚自动跑
-- 索引自动维护脚本
DECLARE @sql NVARCHAR(MAX)='';
SELECT @sql +=
CASE
WHEN avg_fragmentation_in_percent>30 THEN
'ALTER INDEX '+QUOTENAME(i.name)+' ON '+QUOTENAME(OBJECT_SCHEMA_NAME(ips.object_id))+'.'+QUOTENAME(OBJECT_NAME(ips.object_id))+' REBUILD; '
WHEN avg_fragmentation_in_percent>=10 AND avg_fragmentation_in_percent<=30 THEN
'ALTER INDEX '+QUOTENAME(i.name)+' ON '+QUOTENAME(OBJECT_SCHEMA_NAME(ips.object_id))+'.'+QUOTENAME(OBJECT_NAME(ips.object_id))+' REORGANIZE; '
ELSE ''
END
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.index_id>0 AND ips.page_count>1000;
EXEC sp_executesql @sql;
-- 全部表更新统计信息
EXEC sp_updatestats;
PRINT '索引维护+统计更新完成';
|
|