“坏索引”非标准术语,实指两类问题:一是碎片高(avg_fragmentation_in_percent > 30),可用alter index rebuild修复;二是结构损坏(dbcc checkdb报msg 89xx),rebuild无效,须repair_allow_data_loss或还原备份。

SQL Server 中无法靠单个存储过程“自动修复所有坏索引”——因为“坏索引”本身不是标准概念,而 ALTER INDEX REBUILD 只能处理可识别的物理碎片或禁用状态,不能修复逻辑损坏(如页校验和失败、索引键不一致等)。
什么是真正的“坏索引”?先分清两类问题
用户常把“查询变慢”“执行计划走错”归为“索引坏了”,但背后原因完全不同:
-
sys.dm_db_index_physical_stats返回avg_fragmentation_in_percent > 30:这是**碎片高**,ALTER INDEX REBUILD有效 -
DBCC CHECKDB报出Msg 89xx(如 8946、8966):这是**结构损坏**,REBUILD无效,必须用REPAIR_ALLOW_DATA_LOSS或从备份恢复 - 索引被手动
DISABLE:状态为is_disabled = 1,此时REBUILD可启用并重建
如何安全地自动重建高碎片索引?
核心是过滤出真正适合 REBUILD 的索引,并避开系统表、只读文件组、内存优化表等不支持场景:
- 只处理
index_type IN (1, 2)(聚集/非聚集索引),跳过堆(0)、XML、空间、列存储索引 - 排除
is_disabled = 1的索引(需先ENABLE,但REBUILD自动包含该行为) - 检查
has_filter = 1的筛选索引:SQL Server 2012+ 支持在线重建,但低版本需显式加ONLINE = OFF - 对大表考虑加
MAXDOP = 1避免并行阻塞,或用WAIT_AT_LOW_PRIORITY(SQL Server 2014+)减少锁影响
示例判断逻辑(放入存储过程循环中):
SELECT QUOTENAME(s.name) + '.' + QUOTENAME(t.name) AS table_name, QUOTENAME(i.name) AS index_name, ps.avg_fragmentation_in_percent FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ps JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id JOIN sys.tables t ON i.object_id = t.object_id JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE ps.avg_fragmentation_in_percent > 30 AND i.index_id > 0 -- 排除堆 AND i.is_disabled = 0 AND t.is_ms_shipped = 0;
哪些情况绝对不能用 ALTER INDEX REBUILD?
强行执行会报错或无效,必须提前拦截:
- 目标索引在
READ_ONLY文件组上 → 报错Msg 1927 - 数据库处于
READ_ONLY模式 → 报错Msg 3902 - 索引属于内存优化表(
is_memory_optimized = 1)→ 不支持REBUILD,只能ALTER TABLE ... REBUILD - 索引含计算列且底层函数为非确定性(如
GETDATE())→REBUILD失败,报Msg 2729 - 正在做完整备份或日志备份期间,
ONLINE = ON可能因锁升级失败 → 建议加TRY...CATCH并降级为OFFLINE
为什么不能无脑全自动?关键风险点在这里
真正难的不是写循环重建语句,而是判断上下文是否允许重建:
- 重建期间会申请
SCH-M锁,阻塞所有 DML;对大表可能持续数分钟,线上库必须评估窗口期 -
REBUILD会触发完整统计信息更新(即使STATISTICS_NORECOMPUTE = ON),可能引发后续查询重编译风暴 - 如果索引建在
tempdb上(如排序溢出),误判为用户索引会导致脚本崩溃 - 未检查
page_count 的小索引:碎片率虚高,<code>REBUILD得不偿失,应改用REORGANIZE
所以所谓“自动修复”,本质是带条件检查的重建调度器,而非故障自愈系统。逻辑越全,越要留人工确认环节——比如把待处理索引写入临时表,发邮件通知 DBA 审核后再执行。










