存储过程运行一段时间后变慢,大概率是索引碎片恶化所致;需用sys.dm_db_index_physical_stats(limited/sampled模式)查avg_fragmentation_in_percent和page_count,碎片>30%且页数超1000时应rebuild,10%–30%则reorganize,并同步更新统计信息、执行sp_recompile刷新计划。

存储过程运行一段时间后变慢,大概率不是代码变了,而是底层索引碎片在悄悄恶化——尤其当表有高频 INSERT/UPDATE/DELETE 时,碎片会持续累积,导致执行计划退化、I/O 暴增、缓存命中率下降。
怎么确认是索引碎片惹的祸
别猜,直接查 sys.dm_db_index_physical_stats。这个 DMV 是唯一能反映真实物理碎片状态的来源,其他视图或 SSMS 图形界面都可能滞后或失真。
- 必须用
LIMITED或SAMPLED模式扫描,DETAILED在大表上会严重阻塞且耗时过长 - 重点关注
avg_fragmentation_in_percent和page_count:小表(page_count )即使碎片 50% 也影响有限;但一个 50 万页的聚集索引碎片率达 35%,基本就是性能拐点 - 如果发现某张表的查询执行计划里频繁出现
Index Scan替代Index Seek,同时rows字段远大于实际返回行数,大概率是碎片+统计信息陈旧共同作用的结果
重建还是重组?看碎片率和业务窗口
阈值不是教条,但得有依据:
avg_fragmentation_in_percent :不用动,收益远低于开销5% :优先 <code>ALTER INDEX ... REORGANIZE,它在线、不锁表、资源消耗低,适合白天轻量维护-
avg_fragmentation_in_percent > 30%:必须ALTER INDEX ... REBUILD,否则无法真正重排页、更新统计信息、释放空闲空间;若为生产库,务必加WITH (ONLINE = ON)(企业版)或安排在低峰期 - 注意:
REBUILD默认会重置FILLFACTOR为 0(即 100%),高频写入表建议显式指定WITH (FILLFACTOR = 80),预留页内空间减少后续分裂
重建后为什么还是慢?三个容易被忽略的点
重建只是手段,不是终点。常见盲区:
- 没更新统计信息:
REORGANIZE不更新统计信息,REBUILD默认更新,但若用了STATISTICS_NORECOMPUTE = ON选项或手动禁用了自动更新,就得补一句UPDATE STATISTICS - 执行计划没刷新:SQL Server 不会因索引重建自动清除缓存中的旧计划,特别是存储过程中带参数化查询时,可能还在用碎片前的“最优”计划;可对单个过程执行
sp_recompile 'YourStoredProcedureName'强制下次执行重新编译 - 磁盘 I/O 瓶颈被掩盖:碎片降低后,原本被 I/O 吃掉的延迟转成了 CPU 或内存压力,此时应同步检查
sys.dm_exec_query_stats中该存储过程的total_logical_reads和total_worker_time是否同步下降,否则问题可能已转移
碎片重建不是定时任务,而是响应式操作——看到执行计划劣化、avg_page_space_used_in_percent 持续低于 60%、或大批量删除后空间未释放,这时候才该动手。重建本身不难,难的是判断它是否真该发生,以及重建之后有没有真正闭环验证。










