应直接分析 segment statistics 中 object_type = 'index' 的 physical reads 和 logical reads;需下钻 dba_hist_seg_stat,过滤业务用户索引,结合执行计划、clustering_factor 和绑定变量验证是否真为低效扫描。
直接看 segment statistics 里的 physical reads 和 logical reads,但必须过滤 object_type = 'index',否则表和索引混在一起,根本看不出索引是否被高频扫描。
为什么不能只信 SQL ordered by Reads
SQL 报告里排第一的语句,可能读的是表,也可能读的是索引——但它不会告诉你这次读是走 INDEX RANGE SCAN 还是 FULL TABLE SCAN 后回表。更麻烦的是,同一条 SQL 在不同执行中可能走不同路径,而 SQL 报告只汇总 Reads 总量。真正高频扫描索引的信号,藏在段级统计里:physical reads 高 + OBJECT_TYPE = 'INDEX' + OWNER 是业务用户,这三者同时满足才值得深挖。
查 Segment Statistics 要手动加条件
AWR 报告默认的 Segment Statistics 表格只列 top 5,且不区分对象类型。想准确筛出索引,得下钻到基表:dba_hist_seg_stat。关键操作如下:
- WHERE
statistic_name IN ('physical reads', 'logical reads'),别漏掉logical reads—— 它高说明索引在内存里被反复访问,也可能是热点索引 - JOIN
dba_objects时用obj#匹配object_id,但如果是分区索引,优先用dataobj#关联object_id(否则查不到子分区) - 过滤
OBJECT_TYPE = 'INDEX',且OWNER NOT IN ('SYS','SYSTEM','XDB'),避免系统对象干扰判断 - 时间窗口要窄:用两个相邻快照(比如
snap_id BETWEEN 1234 AND 1235),避免跨天聚合把偶发扫描放大成常态
物理读高 ≠ 索引设计错,先看访问模式
一个索引 physical reads 高,可能只是因为它被大量用于主键查找(比如订单号查询),这是合理行为;但如果它的 physical reads 远高于对应基表,就危险了——说明 SQL 在用它做低效范围扫描,或者谓词没走索引(如 WHERE UPPER(name) = 'ABC')。此时要交叉验证:
- 查该索引上最近的执行计划:
V$SQL_PLAN中是否有INDEX RANGE SCAN但access_predicates为空,或只有filter_predicates - 看
dba_indexes的clustering_factor:如果接近表行数,说明数据物理分布和索引顺序严重偏离,范围扫描代价会飙升 - 检查绑定变量值是否导致计划倾斜:某次执行用了极低选择性值,走了索引,后续被缓存复用,实际多数情况不该走
最易忽略的一点:physical reads direct 高的索引通常不是被扫描,而是被并行 DML 或大查询直读——这种读不进 buffer cache,也不会体现在 logical reads 里,单看 physical reads 会低估真实压力。











