AWR报告无法直接反映索引使用次数,需结合Segments by Logical/Physical Reads及Buffer Busy Waits交叉分析,并用ASH定位具体SQL与执行计划,辅以统计信息、索引监控和分区细化验证索引真实有效性。
怎么看索引有没有被真正用上
awr报告本身不直接告诉你“某个索引用了多少次”,它只提供段级(segment-level)的逻辑读、物理读、缓冲区忙等待等聚合数据。真正判断索引是否有效,得把 segments by logical reads、segments by physical reads 和 segments by buffer busy waits 这三块数据交叉比对——重点不是“读得多”,而是“读得值不值得”。比如一个索引段逻辑读排前三,但对应 sql 的 sql ordered by gets 里却找不到高逻辑读的语句,大概率是全表扫描绕过了索引,或者索引被用于低效的范围扫描+回表。
- 优先筛出
object_type = 'INDEX'的段,排除表、LOB、分区等干扰项 - 对比同一时间窗口内该索引的逻辑读与对应基表的逻辑读:若索引逻辑读 > 基表逻辑读 × 2,且没看到明显排序/位图操作,要怀疑是否走了索引但回表代价过高
- 如果该索引在
Segments by Physical Reads排名靠前,但db file sequential read等待事件占比低,说明物理读可能来自 temp 表空间(比如 index fast full scan + sort),而非真实索引块读取
为什么光看AWR的Segment Statistics不够准
Segments by Logical Reads 是按快照区间汇总的,它无法区分“一次高效索引唯一扫描”和“一万次低效索引范围扫描+回表”。更关键的是,它完全掩盖了执行计划变更的影响:同一个索引,在快照 A 里可能是 NL Join 的驱动端,在快照 B 里可能因统计信息过期变成了 Filter + Full Scan,但 Segment Statistics 里读数可能变化不大。
- AWR 不记录每次访问的谓词条件,所以你看不到 “WHERE status = 'X'” 是否真的命中了索引的最左前缀
- 如果索引列上有函数(如
UPPER(name)),即使 SQL 文本里写了索引字段,AWR 也不会把它归到那个索引段下——因为实际访问的是函数索引或根本没走索引 -
Segments by Buffer Busy Waits高,不一定代表索引热块争用;也可能是反向键索引(reverse key index)在高并发插入时导致的叶块争用,这种争用在 Segment Statistics 里表现为索引段等待,但根源不在查询逻辑
用ASH补AWR的盲区:定位具体SQL+执行计划
AWR 告诉你“哪个索引段忙”,ASH(Active Session History)才能告诉你“哪条 SQL、在哪个执行计划分支、为什么用这个索引”。关键是要关联 ash.sql_id → dba_hist_sql_plan → plan_table_output,再过滤 operation = 'INDEX' 的行。
- 查最近1小时索引访问最频繁的 SQL:
SELECT sql_id, COUNT(*) cnt FROM v$active_session_history WHERE session_state = 'ON CPU' OR event LIKE 'db file%' AND current_obj# IN (SELECT object_id FROM dba_objects WHERE object_type = 'INDEX') GROUP BY sql_id ORDER BY cnt DESC FETCH FIRST 5 ROWS ONLY;
- 确认某条 SQL 是否真走索引:用
DBMS_XPLAN.DISPLAY_AWR(sql_id, plan_hash_value)查历史执行计划,重点看Predicate Information里有没有access((表示走索引定位) vs) filter((表示先取数据再过滤)) - 如果 ASH 显示大量会话卡在
enq: TX - index contention,别急着重建索引——先查v$segment_statistics里该索引的gc buffer busy和buffer busy waits是否集中在特定 block,这往往指向主键/唯一索引的高并发插入热点
容易忽略的三个细节
索引利用率分析最容易栽在三个地方:统计信息陈旧导致执行计划误判、索引监控(MONITORING)未开启导致 v$object_usage 为空、以及分区索引的局部性被整体统计掩盖。特别是最后一点:一个全局索引逻辑读很高,但实际只有 1 个分区被高频访问,其余分区全是空转,这时候优化方向应该是分区裁剪,而不是删索引。
-
DBA_INDEXES.monitoring默认是NO,必须手动ALTER INDEX xxx MONITORING USAGE并等至少一次快照周期后才可查v$object_usage - 分区索引要拆开看:
SELECT segment_name, partition_name, logical_reads FROM dba_hist_seg_stat WHERE obj# IN (SELECT object_id FROM dba_objects WHERE object_name = 'XXX_IDX') AND snap_id BETWEEN X AND Y - 如果发现某索引在
Top SQL里完全没出现,但在Segments by Logical Reads里排名很高,极可能是物化视图刷新、DBMS_STATS 收集统计信息、或分区维护操作(如ALTER TABLE ... MOVE PARTITION)在后台触发的隐式访问











