真正拖慢系统的往往是单次物理读爆炸、执行次数低的sql,oltp场景下physical reads/executions超1000需警觉;应结合dba_hist_seg_stat定位读取对象,用dbms_xplan.display_awr验证执行计划,并区分报表与oltp场景。
直接看 sql ordered by physical reads 页面,但别只盯着排名前几条——真正拖慢系统的,往往是那些单次物理读爆炸、执行次数却很低的“定时炸弹”。
Physical Reads per Execution > 1000 就该警觉
OLTP 场景下,Physical Reads / Executions 超过 1000 是明确信号。比如某 SQL 执行 2 次,总 Physical Reads 高达 85 万,基本可断定走了全表扫描且没走索引。
- 计算方式:报告里有
Physical Reads和Executions两列,手动相除(注意Executions为 0 时要跳过) - 警惕低频高读:执行次数少但单次物理读 > 50 万的 SQL,常出现在夜间批量作业或报表中,容易被忽略
- RAC 环境下必须核对
Instance ID,避免把节点 A 的热点误判为全局问题
结合 DBA_HIST_SEG_STAT 锁定真实读取对象
AWR 报告只给 SQL_ID,不告诉你读的是哪张表。得靠 DBA_HIST_SEG_STAT 反查物理读来源。
- 先从报告中复制出问题
SQL_ID,再运行:SELECT object_owner, object_name, operation, options FROM dba_hist_sql_plan WHERE sql_id = '<code>your_sql_id</code>' AND operation LIKE '%TABLE ACCESS%' AND ROWNUM
- 再查这些对象在对应快照区间内的物理读总量:
SELECT owner, object_name, SUM(physical_reads) reads FROM dba_hist_seg_stat s JOIN dba_objects o ON s.obj# = o.object_id WHERE s.snap_id BETWEEN <code>begin_snap</code> AND <code>end_snap</code> AND o.owner IN ('YOUR_SCHEMA') GROUP BY owner, object_name ORDER BY reads DESC; - 重点看
TABLE ACCESS FULL对应的大表,再用select bytes/1024/1024 as mb from dba_segments确认真实大小——别被分区名或视图名误导
验证执行计划是否真缺索引
拿到 SQL_ID 后,必须查它实际走的执行计划,不能只信统计值。
- 用
DBMS_XPLAN.DISPLAY_AWR('<code>SQL_ID') 查历史执行计划,重点比对Rows(预估)和A-Rows(实际),相差超 10 倍说明统计信息不准 - 检查
Predicate Information段落:WHERE 条件字段是否出现在access或filter中?如果只在filter且前面是TABLE ACCESS FULL,就是典型索引未生效 - 留意
NESTED LOOPS外层返回行数是否过大——比如外层驱动表返回 50 万行,内层没走索引,性能必然崩
最易被忽略的是:物理读高未必等于 SQL 写得差。报表类语句扫 10GB 历史表,物理读高是合理的;但 OLTP 语句每次只查几十行却频繁触发物理读,才是真正要命的问题——得看业务场景,不能只看数字。











