无效索引的典型特征是logical_reads极低但physical_reads或db_block_changes异常高,说明几乎不被查询访问却因dml频繁更新持续产生io;需结合awr中top segments by physical reads、dba_hist_seg_stat及dba_hist_sql_plan综合判断。
无效索引不会直接报错,但会让物理读暴增、执行计划退化、甚至拖慢整个实例——awr里最可靠的线索不是“索引没被用”,而是“索引被用了,但io反而更高”。
查“Top Segments by Physical Reads”里带INDEX后缀却高IO的段
AWR报告中没有“无效索引”分类页,但Top Segments by Physical Reads章节会暴露异常:那些OBJECT_NAME以_IDX、_PK、_UK结尾,且PHYSICAL_READS远高于同表其他索引(或历史均值)的行,大概率是无效索引。
- 重点过滤
OBJECT_TYPE = 'INDEX',排除LOBSEGMENT或FUNCTION-BASED INDEX(它们在AWR中常归类异常) - 如果该索引对应表同时出现在
SQL ordered by Physical Reads前几行,且执行计划含FULL TABLE SCAN,说明优化器已弃用它,但应用仍持续维护(如INSERT/UPDATE触发索引更新),造成写放大+读放大双重开销 - RAC环境下务必核对
INSTANCE_NUMBER,避免把节点B的索引维护误判为全局问题
用DBA_HIST_SQL_PLAN确认索引是否真被走,还是“假走”
看到某SQL用了INDEX RANGE SCAN不等于索引有效——预估行数(ROWS)和实际返回行数差10倍以上,就是典型“假走”信号。必须回溯历史执行计划验证:
- 执行
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('sql_id', NULL, NULL, 'ADVANCED')),重点看Operation列是否为INDEX RANGE SCAN,以及Object_Name是否指向目标索引名 - 对比
Cardinality(预估)和A-Rows(实际),若后者远大于前者,说明统计信息不准或索引选择性崩塌(如字段重复率从5%升至95%) - 若计划显示
INDEX FAST FULL SCAN且Bytes接近全表大小,基本等价于全表扫描——这种索引对查询无加速,只增加DML开销
查DBA_HIST_SEG_STAT确认索引访问是否“只写不读”
真正无效的索引,特征是LOGICAL_READS极低但PHYSICAL_READS或DB_BLOCK_CHANGES异常高——说明它几乎不被查询访问,却因DML频繁更新而持续产生IO。
- 运行
SELECT owner, object_name, object_type, logical_reads, physical_reads, db_block_changes FROM dba_hist_seg_stat WHERE object_type = 'INDEX' AND snap_id IN (SELECT snap_id FROM dba_hist_snapshot WHERE begin_interval_time BETWEEN SYSDATE-3 AND SYSDATE) ORDER BY db_block_changes DESC - 重点关注
logical_reads / NULLIF(db_block_changes, 0) 的索引(即每2次块变更才换来1次逻辑读),这类索引99%是冗余的 - 若
physical_reads为0但db_block_changes持续增长,说明索引完全未被查询路径使用,纯属维护负担
重建前必须验证约束依赖与统计信息时效性
盲目ALTER INDEX ... REBUILD可能引发锁表或约束验证失败,尤其在11g中——先确认它是不是主键/唯一约束载体,再检查统计是否过期。
- 跑
SELECT constraint_name, constraint_type FROM dba_constraints WHERE index_name = 'YOUR_IDX_NAME',若返回P或U,则该索引受约束强绑定,11g不支持ONLINE REBUILD,需业务低峰期停写 - 查
SELECT last_analyzed, num_rows, sample_size FROM dba_indexes WHERE index_name = 'YOUR_IDX_NAME',若last_analyzed早于最近一次大批量数据变更(如ETL作业),先DBMS_STATS.GATHER_INDEX_STATS再评估是否重建 - 重建后必须立刻查
DBA_HIST_SQLSTAT确认EXECUTIONS和PHYSICAL_READS比值是否回归正常(OLTP场景理想值应
真正难的是区分“索引失效”和“索引本就不该存在”——前者靠统计刷新或重建能救,后者删掉才是最优解。别只盯着执行计划里有没有INDEX字样,得看它到底为查询省了多少IO,又为DML添了多少麻烦。











