确认索引是否被跳过需检查SQL实际执行计划而非索引状态:先用dbms_xplan.display_cursor查当前计划,再用display_awr比对历史计划,若INDEX RANGE SCAN变为FULL TABLE SCAN即索引未被选用。
怎么确认索引是不是被跳过了
oracle不会主动告诉你“这个索引失效了”,它只会在执行计划里默默换掉你期待的 index range scan,换成 full table scan 或 index full scan。所以关键不是查索引状态,而是查 sql 实际用了什么访问路径。
常见错误现象包括:同一 SQL 在某天突然变慢、db file scattered read 等待飙升、Physical Reads 暴涨但逻辑读没明显变化。
- 先查最近一次执行的计划:
SELECT * FROM TABLE(dbms_xplan.display_cursor('&sql_id', NULL, 'typical'));—— 注意看Operation列是否还有索引相关操作 - 再比对历史计划:
SELECT * FROM TABLE(dbms_xplan.display_awr('&sql_id'));—— 如果 AWR 里旧计划有INDEX RANGE SCAN,新计划变成FULL TABLE SCAN,基本就是索引没被选中了 - 别只看一条 SQL:AWR 报告里的 “SQL ordered by Physical Reads” 模块里排在前几的语句,大概率正在做全表扫描,顺手查它们的计划
为什么执行计划会突然不走索引
索引“失效”本质是优化器认为走索引代价更高,不是索引坏了。AWR 报告里看不到直接原因,但能帮你定位触发点。
典型场景有三类:
-
CUSTOMER_ID = :B2这种绑定变量查询,如果统计信息过期(比如分区表删了分区但没重收集),优化器可能误判数据分布,放弃索引 - 隐式类型转换:
WHERE order_no = 12345,而order_no是VARCHAR2,Oracle 自动转成TO_NUMBER(order_no) = 12345,索引失效 - 谓词失配:建了
INDEX(customer_id, status),但查询只用WHERE status = 'A',复合索引首列没出现在条件里,大概率被跳过
这些线索在 AWR 的 “SQL Plan Hash Value” 对比中会暴露——不同 hash 值意味着执行路径已变,再结合 “Top 5 Timed Events” 看是否伴随 direct path read 或高 db file sequential read,就能闭环验证。
用 AWR 快速定位索引失效的时间点
执行计划切换往往发生在某次快照之后,AWR 是唯一能回溯的窗口。别翻日志,直接查 dba_hist_sql_plan 和快照时间戳。
- 找出该 SQL ID 历史所有 plan hash:
SELECT DISTINCT plan_hash_value, snap_id FROM dba_hist_sqlstat WHERE sql_id = '&sql_id' ORDER BY snap_id; - 关联快照时间:
SELECT s.snap_id, s.begin_interval_time, p.plan_hash_value FROM dba_hist_snapshot s JOIN dba_hist_sqlstat p ON s.snap_id = p.snap_id WHERE p.sql_id = '&sql_id' ORDER BY s.snap_id; - 重点盯住 plan_hash_value 变更的那一组
snap_id,然后去查那段时间的变更记录:业务是否改了表结构?DBA 是否执行过DBMS_STATS.GATHER_TABLE_STATS或TRUNCATE分区?
注意:如果 dba_hist_sql_plan 里查不到旧计划,说明对应快照周期内该 SQL 没被采样到,或已被老化清理——这时得靠 v$sql_plan 查当前内存中的计划,再结合 v$sqlarea.last_active_time 判断活跃时间窗口。
修复后怎么验证索引真被用了
改完统计信息或加了提示,不能只信执行计划输出,得看真实资源消耗是否下降。
- 执行后立刻查
v$sql:SELECT sql_id, plan_hash_value, buffer_gets/executions avg_gets, disk_reads/executions avg_reads FROM v$sql WHERE sql_id = '&sql_id' AND executions > 0;—— 对比修复前后的avg_reads,降一个数量级才算有效 - 生成一份新 AWR 报告(15 分钟粒度),对比 “Segments by Physical Reads” 里该表的排名是否跌出 Top 10
- 如果还高,检查是否真走了索引:在
dbms_xplan.display_cursor输出里找Predicate Information部分,确认access()而非filter()中出现了索引字段
最易忽略的一点:分区表上只收集了全局统计信息,但查询命中某个分区,而该分区的本地统计信息为空——此时优化器仍可能弃用索引。务必确认 DBA_TAB_STATISTICS 中对应分区的 STATTYPE_LOCKED 为空且 LAST_ANALYZED 是近期时间。


















