AWR报告中需结合SQL ordered by Gets/Reads定位高消耗语句,再查其执行计划中Operation是否含TABLE ACCESS FULL;即使有索引,也可能因选择性低、统计信息过期或函数使用等原因导致全表扫描。
怎么看AWR报告里哪些SQL在做全表扫描
awr 报告本身不直接标出“全表扫描”,得从执行计划和统计信息里反推。重点关注 db time 高、buffer gets 或 physical reads 异常大的 sql,再结合 sql ordered by gets 或 sql ordered by reads 部分定位语句。
真正判断是否全表扫描,要查该 SQL 在 AWR 中捕获到的执行计划(在 SQL ordered by Elapsed Time 后附带的 Plan Hash Value 对应部分),找 Operation 列中是否有 TABLE ACCESS FULL。注意:即使有索引,如果优化器判定成本更低,仍可能选全表扫描。
- 别只看
Executions次数多就认为有问题——单次扫描 100 行表和扫描 1000 万行表,影响天差地别 - 检查
Rows Processed和Buffer Gets的比值:若每行消耗 Buffer Gets > 10–20,大概率存在低效访问路径 - 确认该 SQL 是否真的需要返回全部数据(比如
SELECT *+ 无WHERE)
为什么加了索引还是走全表扫描
常见误区是“建了索引就自动走”,但 Oracle 会基于统计信息估算代价。以下情况即使有索引也会选全表扫描:
- 谓词列未在索引最左前缀上(如索引是
(a, b),但查询条件只有WHERE b = ?) - 字段选择性极低(如
gender只有 'M'/'F',索引范围扫描需回表大量数据,不如直接扫表) - 统计信息过期:
DBA_TAB_STATISTICS.last_analyzed老于 7 天,或表数据量突增后未收集 -
WHERE条件用了函数或隐式转换,导致索引失效(如WHERE UPPER(name) = 'ABC'或WHERE id = '123'且id是 number 类型)
验证方式:用 EXPLAIN PLAN FOR + SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY) 对比真实执行计划,注意看 Predicate Information 是否显示 index used。
如何让关键查询避开全表扫描
不是所有全表扫描都该被干掉,但对高并发、大表、OLTP 场景下的高频查询,必须干预。核心思路是让优化器「愿意」且「能够」用索引:
- 确保 WHERE / JOIN / ORDER BY 中涉及的列,组合成高选择性、覆盖度高的复合索引(例如
WHERE status = ? AND created_date > ? ORDER BY priority,可建(status, created_date, priority)) - 避免在索引列上使用函数;如必须大小写模糊匹配,改用函数索引:
CREATE INDEX idx_name_upper ON t (UPPER(name)) - 对小结果集但大表扫描的场景(如分页查第 100 页),考虑用
ROWNUM或OFFSET ... FETCH(12c+)配合索引排序,而非先扫全表再ORDER BY ... ROWNUM - 临时压制全表扫描(仅调试):加 hint
/*+ INDEX(t idx_name) */,但上线前务必验证执行计划稳定性
全表扫描优化后怎么验证效果
别只信执行计划“变绿了”,要看真实负载变化:
- 对比优化前后同一 SQL 在 AWR 中的
Buffer Gets/Execution和CPU per Execution:下降 50%+ 才算有效 - 查
v$sql_plan_statistics_all(需开启STATISTICS_LEVEL = ALL)看starts和buffer_gets实际值,排除执行计划缓存误导 - 留意并发影响:单条 SQL 快了,但如果加索引导致 DML 变慢(尤其高频率 INSERT/UPDATE),整体吞吐可能反而下降
- 定期重收集统计信息(
DBMS_STATS.GATHER_TABLE_STATS),否则优化器很快又“想不开”
最易被忽略的是:大表加索引本身耗资源,且索引维护成本随 DML 增长。优化一个 SQL 的全表扫描,有时不如砍掉它本不该存在的需求——比如那个每天凌晨跑一次、导出全表的报表脚本。


















